Design Your Liquibase Project

Your changelog is the core of your Liquibase project. While you can use one changelog for all your deployments, this may not scale well. Instead, you can use multiple changelogs for different purposes. To organize multiple changelogs, you can use the include and includeAll tags to create a root (main/parent) changelog and one or more nested (child) changelogs. This document explains some best practices on structuring your changelogs.

Prerequisites

If you're new to Liquibase, make sure you understand the fundamentals before creating your own project:

  1. Install Liquibase – Download Liquibase on your machine
  2. Get Started with Liquibase – Learn how to use Liquibase with an example database

Create a project

The Get Started guide linked previously shows you how Liquibase deployments work using a sample database and changelog. However, your organization's needs go beyond a demonstration. To create a new Liquibase project, run the init project command in your command line:

liquibase init project

This generates a new changelog and liquibase.properties file that you can customize and expand. You can use the default settings or specify your own. For more information on command arguments, see the init project page.

Choose a schema design pattern

You can use Liquibase in two ways:

  • Shared schema: All developers work in a single, shared database schema
  • Multi-schema: Each team works in a different database schema

To choose what's best for your organization, consider the processes your developers currently use to deploy changes to the database.

Shared schema

If you use a shared schema with Liquibase, all your developers work on the same database schema.

Using a single schema can make it simple to design, manage, standardize, and query your database. However, using a single schema in a large database may become confusing as you create more objects. If you need to write long queries to navigate complex object structures in a single-schema database, the queries may negatively affect performance.

It's important for everyone using the same schema to agree on a deployment process. For example, one team may modify a shared script without realizing that it's a dependency for another team's automation. One solution is to keep all schema content in a single repository owned by one team. When other teams want to make changes, they submit pull requests from their source control tool. Alternatively, each team can work in their own directory in the schema and try to minimize cross-team dependencies.

Multiple schemas

If you use multiple schemas with Liquibase, you create one or more schemas in your database for different teams.

Using multiple schemas may make it easier to organize your data, which may be helpful in large databases containing many unique data structures. Also, using multiple schemas may prevent conflicts with source control merges or broken dependencies, help you store data backups, and let you customize user and application permissions for each schema.

However, using multiple schemas means you may have to duplicate some of your data, which can make consistency more difficult. Depending on your automation tooling and workflow, you may also need to call Liquibase multiple times to send your code to production, which requires extra maintenance from your DevOps team.

Choose a changelog structure

When designing your Liquibase project, it is a best practice to create a simple root changelog that you don't have to modify directly. Instead, you can make changes to each of the changelogs beneath it, which you can reference with the include and includeAll tags. The nested changelogs can be modified and deployed independently, but you can also deploy all changes in a single update from the root changelog. There are two common changelog design patterns, object-oriented and release-oriented. The design you choose should correspond to the structure of your existing code repository and workflow.

Object-oriented

Tip: For most organizations, it is a best practice to use an object-oriented changelog design pattern.

In an object-oriented changelog structure, you create one changelog per object or object type. For example:

com
  example
    db
      changelog
        changelog-root.xml
        changelog-index.xml
        changelog-procedure.xml
        changelog-table.xml
        changelog-view.xml

In this example, we have a root changelog called changelog-root.xml which contains include tags referencing changelog-index.xml, changelog-procedure.xml, changelog-table.xml, and changelog-view.xml.

Alternatively, you can create directories for each type of object, each containing one or more changelogs per particular database object:

com
  example
    db
      changelog
        changelog-root.xml
        changelog-indexes
          my-favorite-index.xml
          that-other-index.xml
        changelog-tables
          employees.xml
          customers.xml

In this example, we have a root changelog called changelog-root.xml which contains includeAll tags referencing the directories /changelog-indexes and /changelog-tables. The changelogs you modify regularly are contained in the nested directories rather than in the overarching /changelog directory.

These designs are useful because they allow you to easily see changes made over time to specific objects (like a particular table) or groups of objects (like all tables in your database). An object-oriented changelog format makes it easier to roll back changes to particular objects without worrying about unrelated changes in the changelog.

Having a more detailed changelog structure can also potentially simplify the Source Control Management (SCM) process. If all your developers work in the same changelog, they may experience source control merge conflicts more frequently. By separating your changelogs into use-cases for different objects, developers are likely to be working in different files and therefore will not overwrite each other's code.

Release-oriented

If a release-oriented changelog structure, you create one changelog per release or release group. For example:

com
  example
    db
      changelog
        changelog-root.xml
        changelog-1.0.xml
        changelog-1.1.xml
        changelog-2.0.xml

In this example, we have a root changelog called changelog-root.xml which contains include tags referencing changelog-1.0.xml, changelog-1.1.xml, and changelog-2.0.xml.

Alternatively, you can create directories for each major release (or other release group), each containing one or more changelogs per minor release:

com
  example
    db
      changelog
        changelog-root.xml
        changelog-1.x
          changelog-1.0.xml
          changelog-1.1.xml
        changelog-2.x
          changelog-2.0.xml

In this example, we have a root changelog called changelog-root.xml which contains includeAll tags referencing the directories /changelog-1.x and /changelog-2.x. The changelogs you modify for a particular release are contained in the nested directories rather than in the overarching /changelog directory.

A release-oriented changelog structure lets you bundle all content associated with a release in one place. However, this design can be difficult to maintain. If you want to see changes made to a particular object over time, the release-oriented structure requires you to look in multiple locations for that information.

Additionally, having a single changelog per release (even minor release) may lead to more frequent source control merge conflicts. You can solve this by creating multiple changelogs within each release directory, such as /changelog-1.0/indexes.xml and /changelog-1.0/tables.xml. However, this structure may be more complicated to navigate and maintain than a pure object-oriented changelog design.

Prepare for deployment

  • When you use the includeAll tag in your root changelog, you must ensure your file naming convention is consistent. Liquibase sorts pending scripts in alphanumeric order and then deploys them in that order.
  • You can specify filtering logic to control which changesets in your changelog are executed at runtime by using preconditions, contexts, labels, and other changelog attributes like runAlways and runOnChange. For more information, see Changelog Attributes.
  • When you publish updates to your software, it is a best practice to release separate artifacts for each step in your development pipeline, such as artifacts for dev, qa, and prod. You can easily version artifacts and run checks on them with automation tools. Alternatively or in addition, you can use contexts to group changesets into environments and labels to mark changesets as belong to specific features. Simulating environments with branches in source control is not recommended.

Next steps

  1. Liquibase Database Tutorials – Configure Liquibase to work with your own database
  2. Best Practices – Read about best practices to follow with Liquibase