Implementing Database Migration

1. Implementing Schema Versioning

ToolConvention
FlywayV1__create_users.sql
LiquibaseXML/YAML/JSON changelogs
AlembicPython SQLAlchemy
Knex/SequelizeJS/TS migrations

2. Implementing Migration Scripts

Example: Flyway migration

-- V20260101_001__create_orders.sql
CREATE TABLE orders (
  id BIGSERIAL PRIMARY KEY,
  customer_id UUID NOT NULL,
  status VARCHAR(20) NOT NULL,
  total_cents BIGINT NOT NULL CHECK (total_cents >= 0),
  placed_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_orders_customer ON orders(customer_id, placed_at DESC);

3. Implementing Rollback Scripts

ApproachDetail
Forward-onlyWrite a new migration to undo
Liquibase rollbackDefine rollback in changeset
Backup + restoreFor destructive changes
Warning: Rollback scripts often can't restore lost data — prefer forward-fix migrations.

4. Implementing Baseline Migration

UseDetail
Existing DBMark current state as baseline
Flywayflyway baseline -baselineVersion=1
EffectSkip migrations ≤ baseline

5. Implementing Repeatable Migrations

NamingRun When
R__views.sqlChecksum changes
Use forViews, functions, procedures

6. Implementing Callback Handlers

CallbackWhen
beforeMigratePre-run all migrations
afterMigratePost-run all
beforeEachMigratePer migration
afterEachMigratePer migration

7. Implementing Migration Validation

CheckTool
Checksum mismatchFlyway validate
Out-of-order detectionFlyway config
Lintingsquawk, sqlfluff
Dry-runCI on shadow DB

8. Implementing Out-of-Order Migrations

CaseDetail
Long-running branchLower-number migration appears later
Flyway flagoutOfOrder=true
RiskApply order differs across envs

9. Implementing Placeholder Replacement

Example: Per-env placeholder

-- placeholders: schema=acme
CREATE TABLE ${schema}.orders ( ... );

10. Implementing Migration Testing

StepTool
Spin disposable DBTestcontainers PostgreSQL
Run migrationsFlyway/Liquibase API
Assert schemaQuery information_schema
Test data preservedSeed before, verify after

11. Implementing Data Migrations

PatternDetail
Backfill in batchesUPDATE ... LIMIT loop
Read replicaAvoid impacting production
IdempotentSafe to re-run
Track progressCursor table

12. Implementing Zero-Downtime Migrations

Expand-Contract Pattern

  1. Expand: add new column/table (nullable)
  2. Deploy app version writing both old and new
  3. Backfill data into new column
  4. Deploy app version reading from new only
  5. Contract: drop old column in later release
ConcernMitigation
Long-locking DDLUse CONCURRENTLY for indexes (PG)
Renaming columnAdd new + sync + drop old
Type changeNew column + backfill + cutover