The following best practices provide guidance on the primary Liquibase components and workflow.

Organize your Changelogs

1. Define the directory structure

The most common way to organize changelogs is by major release.

Make sure to store your changelogs in source control, preferably near your database access code

In this example, we will use com/example/db/changelog.

 com
    example
       db
          changelog
             db.changelog-root.xml
             db.changelog-1.0.xml
             db.changelog-1.1.xml
             db.changelog-2.0.xml
          DatabasePool.java
          AbstractDAO.java 

2. Set up the root changelog and included changelog files

The db.changelog-root.xml file will automatically include the changelogs for each release in the alpha-numeric order.

Note: The db.changelog-root.xml file is the changelog name that you will pass to all Liquibase calls.

For the example above, the changelog would look like the following:

<?xml version="1.0" encoding="UTF-8"?>   
<databaseChangeLog
   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:pro="http://www.liquibase.org/xml/ns/pro"
   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
      http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd
      http://www.liquibase.org/xml/ns/pro 
      http://www.liquibase.org/xml/ns/pro/liquibase-pro-4.1.xsd">  

    <includeAll path="com/example/db/changelog/"/>  
</databaseChangeLog> 

The included changelogs can be in any format (including formatted SQL). Choose the appropriate format that best fits you and your team’s situation.

Use a SQL changelog when your team

  • Is already familiar with SQL
  • Wants to get the benefits of using Liquibase without needing to learn a new way to specify database changes

Use a Model-based changelog (XML, JSON, or YAML) when your team

  • Wants to deploy a single set of changes to different types of databases
  • Wants to take advantage of automated rollback of new database objects
  • Wants to take advantage of Liquibase diff and diffChangelog functionality

Here are examples of the different changelog formats for the db.changelog-1.0 file:

--liquibase formatted sql
        
--changeset nvoxland:1
create table person (  
    id int primary key,
    name varchar(255)  
);

 

<?xml version="1.0" encoding="UTF-8"?>   
<databaseChangeLog
   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:pro="http://www.liquibase.org/xml/ns/pro"
   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
      http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd
      http://www.liquibase.org/xml/ns/pro 
      http://www.liquibase.org/xml/ns/pro/liquibase-pro-4.1.xsd">

   <changeSet  author="nvoxland"  id="1">
      <createTable tableName="person">
         <column  name="id"  type="INTEGER">
            <constraints  nullable="false"  primaryKey="true"  unique="true"/>
         </column>
         <column  name="name"  type="VARCHAR(255)" />
      </createTable>
   </changeSet>

</databaseChangeLog>

 

{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "1",
        "author": "nvoxland",
        "changes": [
          {
            "createTable": {
              "tableName": "person",
              "columns": [
                {
                  "column": {
                    "name": "id",
                    "type": "int",
                    "autoIncrement": true,
                    "constraints": {
                      "primaryKey": true,
                      "nullable": false
                    },
                  }
                },
                {
                  "column": {
                    "name": "name",
                    "type": "varchar(255)"
                  }
                }
              ]
            }
          }
        ]
      }
    }
  ]
}

 

databaseChangeLog:  
  -  preConditions:  
    -  runningAs:  
        username:  liquibase  

  -  changeSet:  
      id:  1  
      author:  nvoxland  
      changes:  
        -  createTable:  
            tableName:  person  
            columns:  
              -  column:  
                  name:  id  
                  type:  int  
                  autoIncrement:  true  
                  constraints:  
                    primaryKey:  true  
                    nullable:  false  
              -  column:  
                  name:  name  
                  type:  varchar(255)

 

Establish team changelog standards

1. One change per changeset

We strongly encourage having only one change per changeset. This makes each change atomic within a single transaction. Each changeset either succeeds or fails. If it fails, it can be corrected and redeployed until it succeeds. Multiple independent changes in one changeset create a risk that some changes deploy while a later change fails. This leaves the database in a partially deployed state which requires manual intervention to correct.

The exception is when you have several changes you want to be grouped as a single transaction – in that case, multiple statements in the changeset if the correct choice.

2. Define the team’s changeset ID format

Choose a changeset ID that works for you. While it can be any string, we advise using an increasing number sequence starting at 1. Remember that each changeset ID needs to be unique within the changelog.

3. Document unclear or complicated changesets

Most of the time, changesets are self-documenting.

However, remember to use <comments> for any changesets where you need to explain non-obvious or complicated database changes to other developers.

4. Have a rollback plan

Write changesets so they work with Liquibase rollback.

Make sure to test rollbacks in development to ensure the production rollback is safe and predictable.

5. Manage your reference data

Leverage Liquibase to manage your reference data. Environment separation (DEV, QA, PROD) can be achieved using Liquibase contexts. This functionality is helpful in the following situations:

  • When you have test data that should only get deployed to QA environments
  • When managing application configuration data – country table, application configuration data, etc
  • When deplying data-fixes specific to the pre-production and production environments

Manage stored logic

Typically, changesets in Liquibase remain untouched after they are deployed. Subsequent database changes are made when new ‘roll forward’ changesets are added to the end of the changelog.

However, stored logic (Stored Procedures, Functions, Packages, Triggers, etc.) acts more like application code than database schema changes. They are better managed similar to source code where you continually make updates to a single source file for each unit of stored logic. This also allows you to better see changes over time using standard git tools.

We recommend maintaining a separate changelog dedicated to stored logic. The combination of “create or replace” in your stored logic sql along with runOnChange="true" as an attribute on the changeset allows you to ensure changes get deploy when and only when a file changes. Specifically, setting this flag forces Liquibase to check if the changeset was modified. When it detects a modification, Liquibase deploys the (updated) change.

Develop software using this standard workflow

1. Using your favorite IDE or editor, create a new local changeset containing the change

2. Run liquibase update to execute the new changeset

3. Perform the corresponding changes in the application code

4. Test the new application code together with the database change

5. Commit both the changeset and the application code to source control