Automating “validate before apply” is basically: (1) static checks + (2) dry-run SQL generation + (3) run against an ephemeral DB + (4) gate merge/deploy.
Below are the best-practice ways teams do it for Liquibase and Flyway.
1) Static validation in CI (fast, no DB)
Liquibase
Run on every PR:
liquibase validate
Add an extra “render SQL” check:
liquibase updateSQL > /tmp/migration.sql
This is your “compile step” for migrations. Fail the build if it errors.
Flyway
Run on every PR:
flyway validate
What it catches:
- checksum mismatches
- missing migrations / version gaps
- pending/failed migration history issues (when connected to a DB)
You can also generate a dry run script:
flyway migrate -dryRunOutput=/tmp/migration.sql
) Validate against a clean ephemeral DB (the real gate)
Static checks don’t detect:
- DB-specific syntax errors
- locking/non-transactional statements
- constraint violations on real data
- ordering problems that only show up at execution time
Pattern
- Spin up Postgres/MySQL via Testcontainers (or docker-compose) in CI
- Apply migrations
- Optionally run smoke queries / app startup
Liquibase
liquibase update
liquibase status --verbose
Flyway
flyway migrate
flyway info
This is the closest thing to “unit tests for schema”.
3) Policy checks as automated gates
These prevent “dangerous but valid” migrations.
Common rules:
- No
DROP TABLE/DROP COLUMNoutside “contract” phase - No long-running data backfills in migrations (or must be chunked)
- No
CREATE INDEXwithoutCONCURRENTLY(Postgres) if table is large - No non-transactional DDL in the same deployment step (where it matters)
How to implement:
- A simple SQL linter step (regex-based is often enough)
- Or a custom script that inspects generated
updateSQL/dryRunOutput - Block PR if violations are found
4) “Drift detection” (optional but very senior)
You can detect if DB ≠ expected schema:
Liquibase
diff/diffChangeLogbetween a reference schema and a target DB (powerful, but needs discipline)
Flyway
- Strict
validatein all environments + forbid modifying applied migrations - Optionally run
infoand fail if there are unexpected pending migrations in prod
5) Where to wire it in CI/CD (practical)
PR pipeline (fast):
validateupdateSQL/dryRunOutputgeneration- Run migrations on ephemeral DB
Deploy pipeline (safe):
- run the same validation steps
- run migrations once (single “migration runner” job/pod)
- deploy app
Interview-ready answer (what to say)
“We automate changelog validation by running Liquibase/Flyway
validateon every PR, generating a dry-run SQL to catch parsing issues early, and then applying migrations to an ephemeral DB in CI (Testcontainers/docker) as the real gate. We also enforce policy checks on the generated SQL for dangerous operations and keep validation enabled in all environments to prevent drift.”