# 9. Liquibase: anatomy of an update [← 8. Concurrent startup and locking](08-concurrent-startup-and-locking.md) · [Index](../README.md) · Next: [10. Rollback: auto-generated vs explicit →](10-rollback-auto-generated-vs-explicit.md) Liquibase's unit of change is a **changeset** inside a **changelog** — one YAML (or XML, or JSON, or SQL) document listing every change in order, rather than one file per change. This module's main changelog is [`db.changelog-master.yaml`](../src/main/resources/db/changelog/db.changelog-master.yaml); [`LiquibaseHappyPathTest`](../src/test/java/com/ankurm/dbmigrations/liquibase/LiquibaseHappyPathTest.java) uses a small inline one — a `createTable` changeset, then an `insert` changeset — run through the classic [`Liquibase`](../src/test/java/com/ankurm/dbmigrations/liquibase/LiquibaseTestSupport.java) facade directly, with no Spring involved, so the mechanics are visible without an application context in the way. ``` databasechangelog: ID | AUTHOR | FILENAME | ORDEREXECUTED | EXECTYPE -----------------+--------+-------------+---------------+--------- 1-create-account | ankurm | master.yaml | 1 | EXECUTED 2-seed-account | ankurm | master.yaml | 2 | EXECUTED account table: ID | OWNER ---+------------------ 1 | Katherine Johnson ``` (from [`docs/output/09-liquibase-happy-path.txt`](output/09-liquibase-happy-path.txt)) `DATABASECHANGELOG` is Liquibase's equivalent of `flyway_schema_history`, but the identity of a changeset is different in kind from Flyway's version numbers: it's the triple of `id`, `author`, and the changelog `filename` it was declared in. Two changesets with the same `id` in two *different* files are different changesets to Liquibase; the same `id` twice in the *same* file is a configuration error. There is no numeric ordering at all — order comes purely from position in the changelog, top to bottom. master.yaml changeSet id: 1-create-account changeSet id: 2-seed-account DATABASECHANGELOG id=1-create-account, author=ankurm, filename=master.yaml → EXECUTED id=2-seed-account, author=ankurm, filename=master.yaml → EXECUTED ## Going deeper - **`update()` and the newer command-framework path.** Liquibase 5's classic `Liquibase.update(...)` facade internally delegates to a `CommandScope`/`UpdateCommandStep` implementation rather than the older direct-execution path — visible in every stack trace in this module (chapter [11](11-liquibase-locking.md) has a full one). It doesn't change behaviour for straightforward changesets, but it's worth knowing when a stack trace looks unfamiliar next to older Liquibase tutorials. - [Liquibase changelog structure](https://docs.liquibase.com/concepts/changelogs/home.html) (`rel="nofollow"`) — the full list of supported changelog formats and the changeset identity rules referenced above. - Liquibase also supports **preconditions** and **contexts/labels** for conditionally running changesets — out of scope for this module, but the natural next thing to read once changesets and changelogs make sense.