Database Migration Checklist

Chapter 1 of 11 · 4 checks · 2 min

Plan and classify the change

Checks that the migration is tracked, small, and that you know what each statement will do to a live table before you run it.

Show
  1. The migration is a versioned file applied by a migration tool, not a manual queryMust

    Why it matters and how to check

    Schema changes typed into a production console are not reviewed, not repeatable and not recorded anywhere the next engineer will look. A migration tool (Flyway, Liquibase, Alembic, Django, Rails, Prisma, golang-migrate and similar) applies numbered files in order and records which ones ran.

    To verify: confirm the change exists as a file in the repository, that it went through a pull request, and that the tool's history table in staging shows it applied. Compare the tool's history table in production with the files in main to confirm nothing was applied by hand.

  2. For every statement, you know the lock it takes and whether it rewrites the tableMust

    Why it matters and how to check

    The same-looking statements behave very differently. The PostgreSQL docs say ALTER TABLE takes an ACCESS EXCLUSIVE lock unless noted otherwise, which blocks even plain SELECTs, while adding a column with a non-volatile default is fast and changing a column's type normally rewrites the table and its indexes. MySQL's online DDL tables list, for each operation, whether it is instant, in place, rebuilds the table and permits concurrent DML.

    To verify: write next to each statement in the pull request its lock level, whether it rewrites or scans the table, and the documentation row you took that from. Any statement marked as a rewrite or full scan on a large table moves to the safer pattern in the chapters below.

  3. Schema changes and data backfills are separate migrationsShould

    Why it matters and how to check

    A migration that adds a column and then updates every row in the same transaction holds its locks for the whole update, cannot be paused and is hard to retry halfway. Splitting them lets the schema change finish in milliseconds and the backfill run in batches at its own pace.

    To verify: read the migration files. DDL statements and large UPDATE or INSERT ... SELECT statements should not share a file or a transaction. Backfills should be a separate script or job that can be stopped and resumed.

  4. Each release carries at most one risky migrationNice to have

    Why it matters and how to check

    When a deploy contains a rename, two new indexes and a backfill, and latency spikes, nobody knows which one caused it, and rolling back undoes everything. Small, separate steps are easier to watch and to abandon.

    To verify: list the migrations in the release. If more than one rewrites a table, builds a large index or touches a large table's data, split them across releases or at least across separate, observed deploy steps.