<!-- Generated from product/migrations.html at build time. Do not edit. -->

> Canonical: https://antifailure.dev/product/migrations

> Pending migrations rehearsed on a branch with production's shape: per-statement timing, the strongest lock held per table, table rewrites, and query plans before and after.

# Catch exclusive locks before they take checkout down.

The flagship module. A fresh branch carrying production's shape applies the pending migrations while a second connection samples what is locked, then reports the strongest mode held per table, how long it was held, whether another session was left waiting on it, which tables were rewritten, and how the query plans moved.

rehearsal branch · observer connection at 250ms

migration rehearsal · lock exposure

An exclusive lock on subscriptions holds for 27 seconds. The rehearsal reports it before it ships.

## A 27-second lock is a finding. Not a line in a log nobody reads.

- ACCESS EXCLUSIVE the strongest mode held on subscriptions, sampled every 250ms 27.4s

- Blocked another session a second session was seen waiting on the lock while it was held Yes

- Table rewrite reported by Postgres, not inferred from the statement Yes

- Plan change EXPLAIN before and after, on production's own shape Seq Scan

three connections · one measured lock window

postgres · concurrent observation

Example finding A rehearsal of one migration, with the numbers chosen. The measurements are the ones af insights takes: lock mode and hold time from pg_locks, rewrites from Postgres, plans from EXPLAIN.

## Measured, not inferred. The lock comes from pg_locks and the rewrite from Postgres itself.

Staging with a handful of rows will not show an exclusive lock or a table rewrite. A sampler on its own connection watches pg_locks and pg_stat_activity while the migration runs, because the session running it cannot see its own lock until the statement returns, which is exactly when the interesting part is over.

no timing inferred from SQL text

af insights · evidence provenance

## Failures conventional tests miss. The engine measures what staging cannot.

- Locks The strongest mode held per table, how long, and whether another session was left waiting on it.

- Rewrites Full table rewrites, reported by Postgres rather than guessed from the SQL.

- Plans EXPLAIN before and against the migrated branch, on production's own shape.

- Statements Per-statement duration, so the slow one in a batch is named.

- Lint Missing lock timeouts, constraints added without NOT VALID, index builds that are not concurrent, backfills sharing a transaction with the schema change, and the rewrites and offline table operations. Each finding reaches the pull request under its own rule name with the fix attached, because a finding called migration_lint tells nobody what to change.

- Comparison A saved report from an earlier run, compared against this one.

## Safer pattern: expand-and-contract. The lint rule carries the fix, not only the complaint.

Switch the film to expand-and-contract. The strongest lock drops to 0.4s, nothing is left waiting on it, and the table is not rewritten.

schema stays readable across deploys

- 01 Expand add nullable plan_id_v2 0.4s lock

- 02 Backfill copy values in batches no rewrite

- 03 Dual-read read both schema shapes compatible

- 04 Contract drop old column later later deploy

subscriptions · compatibility window

- Expand Add a second column of the new type, nullable. Old rows stay readable by both binaries.

- Backfill Copy values across in batches so checkout never waits on ACCESS EXCLUSIVE.

- Dual-read Deploy code that reads both shapes before you tighten the schema.

- Contract Drop the old column in a later migration, once nothing reads it any more.

- Solutions The teams who feel this first. Read

- Load Traffic sent at the same production-shaped branch. Read

- Invariants docs The subscriptions demo in full. Read

## Know what happens before you deploy.

Create a disposable production twin for every risky change. Catch migration failures before they reach customers.
