Skip to content
Community content. Review instructions before giving them to an AI agent — treat modules like open-source code.

Migrations Discipline

How to change a live schema safely: backward-compatible rollout steps, no same-migration drops, and lock-aware changes on large tables.

Mby @markdownersPublished July 12, 2026 · Updated August 21, 2026 · ~2 min read

0 downloads · Used in: SaaS starter context

Every schema change is a migration, and every migration follows the same discipline as any other code change: reviewed, tested, and safe to run against real data. Assumes Database Design Basics@markdowners/database-design-basics; see Postgres Conventions@markdowners/postgres-conventions for engine-specific type choices referenced below.

Migrations are the only way to change schema

  • Never make a manual, ad-hoc schema change directly against any shared environment (staging, production) — every change goes through a migration file, checked into version control, applied the same way in every environment.
  • Every migration file is immutable once merged/deployed — to fix a mistake, write a new migration that corrects it, never edit a migration that's already been applied anywhere real.

Backward compatibility during rollout

  • Assume the old and new application code can both be running against the database simultaneously during a deploy (rolling deploys, multiple instances mid-rollout) — a migration that breaks the currently-running old code before the new code deploys causes an outage during the deploy window itself, not after.
  • Add a new nullable column, then backfill, then add NOT NULL in a later migration — never add a NOT NULL column with no default directly to a table with existing rows in one step; it either fails outright or locks the table for the backfill duration.
  • When renaming a column, do it as: add new column → dual-write in application code → backfill → switch reads → remove old column, across multiple deploys — never a single-step rename that old code can't tolerate mid-deploy.

Destructive changes

  • Never drop a column or table in the same migration that stops using it — deploy the code change that stops using it first, confirm it's live and stable, then drop the now-unused column/table in a follow-up migration.
  • Treat every DROP, TRUNCATE, and irreversible data transformation as requiring a second reviewer's explicit sign-off before it runs against production data.

Locking and large tables

  • Be aware that some schema operations take a table-level lock for their duration (adding certain constraints, some column additions on older engine versions) — on a large, high-traffic table this can cause a visible outage; use the engine's documented online/concurrent variants where available.
  • Run migrations against a realistic data volume in staging before assuming a migration is "quick" — a migration that's instant on a 500-row dev table can lock a 50-million-row production table for minutes.

Reversibility and testing

  • Write a rollback (down) migration alongside every forward migration where the engine/tooling supports it, and actually test the rollback path, not just the forward path.
  • Run every migration against a copy of production-shaped data (or a realistic seed) in CI before it's mergeable, not just against an empty local database.

Requires

You might also add

Badge

Link back to this module from your own README.

Get it on Markdowners
[![Get it on Markdowners](https://markdowners.com/mdstack-badge.svg)](https://markdowners.com/m/markdowners/migrations-discipline)

Comments (0)

Sign in to comment. Sign in

No comments yet. Be the first to add one.

Discussions about this module

No discussions about this module yet.

Start a discussion