Database migration checklist

Fifteen checks around a schema or data migration, ordered by when the damage becomes expensive to undo. The first group runs before anything is applied, because a migration you cannot reverse and a backup you have never restored are the same risk wearing different names. The second group runs on the copy, where a wrong answer costs nothing. The third group runs after the migration lands, and it exists because the failures that follow a migration are quiet ones: a constraint that was never enforced, an index the planner stopped using, a timestamp column that changed meaning. Every check on this list was written against SQLite on one machine, and each page names the client it ran through, because two clients reading the same file disagree more often than the documentation suggests.

15 checksabout 139 min end to end

Before you apply anything

  • How to verify a database backup
    A backup is a claim until it has been restored, and a truncated dump restores with exit 0 into an empty database that an integrity check calls sound.
    10 min
  • How to check database schema version
    The migration table is the only record of where the database actually is, and it is the first thing to disagree with what the repository says should have run.
    8 min
  • How to test database migration
    The down path is the one nobody runs until it is needed, and a migration that cannot be reversed turns a small mistake into an outage.
    12 min
  • How to check table schema
    The schema as the engine reports it, not as the migration file describes it, is the baseline every post-migration comparison is measured against.
    6 min
  • How to verify row counts after migration
    Counts taken before the change are the only counts you can compare against afterwards, and taking them later means guessing at what was lost.
    8 min

On the copy, where a wrong answer is free

  • How to compare database schemas
    Staging and production drift by a column at a time, and a migration written against one of them fails on the other in a way no test catches.
    10 min
  • How to check data integrity after migration
    Row counts match while values are wrong, and a checksum over the columns that matter is what separates "the rows arrived" from "the data arrived".
    12 min
  • How to check duplicate rows in sql
    A migration that adds a unique constraint fails on data that was legal until that moment, so the duplicates have to be found before the constraint is written.
    7 min
  • How to test unique constraint
    A unique column accepts any number of NULLs, so a constraint can be in force and still permit the duplicates it was added to prevent.
    8 min
  • How to check foreign key
    Enforcement depends on the client that opened the connection, not on the schema, so a declared key can be enforced in one process and ignored in another against the same file.
    9 min

After it lands

  • How to check database integrity
    A structural check and a constraint check answer different questions, and a file can pass one while the other finds orphaned rows.
    10 min
  • How to check if a table has an index
    A migration that rebuilds a table takes its indexes with it, and the ones created by constraints do not appear in the schema dump.
    5 min
  • How to check if a query uses an index
    A plan can change with the data untouched, and an index that was chosen yesterday can be skipped today for the same statement.
    12 min
  • How to find slow queries
    The query that got slower is rarely the one the migration touched, and finding it a week later means reading the change through a production incident.
    12 min
  • How to check timezone stored in database
    A column that changed between a local and an absolute timestamp keeps returning plausible values, and the error is a fixed number of hours that nobody notices until a date boundary.
    10 min

Take it with you

The file is the same list as Markdown checkboxes, ready to paste into a release ticket or a pull request description.

Ticks are kept in this browser only. They are not sent anywhere and other people do not see them.