Chapter 32: Schema Migrations Without Downtime

Production schemas evolve. Adding columns, changing types, creating indexes — all must happen without breaking running applications.

Safe vs Dangerous Operations

OperationSafe?Why
ADD COLUMN (nullable, no default)✓ SafeInstant metadata change
ADD COLUMN with DEFAULT✓ Safe (PG 11+)Default stored in catalog, not rewritten
DROP COLUMN⚠️ CarefulSafe in PG (marks invisible), but app must not reference it
ALTER COLUMN TYPE✗ DangerousRewrites entire table, locks it
ADD NOT NULL constraint⚠️ CarefulScans entire table to validate
CREATE INDEX✗ Blocks writesUse CONCURRENTLY instead
CREATE INDEX CONCURRENTLY✓ SafeDoesn't lock table (slower to build)

Safe Migration Pattern

-- Step 1: Add nullable column (instant, no lock)
ALTER TABLE users ADD COLUMN phone TEXT;

-- Step 2: Backfill data (in batches to avoid long transactions)
UPDATE users SET phone = 'unknown' WHERE phone IS NULL AND id BETWEEN 1 AND 10000;
UPDATE users SET phone = 'unknown' WHERE phone IS NULL AND id BETWEEN 10001 AND 20000;

-- Step 3: Add constraint (after all rows have values)
ALTER TABLE users ADD CONSTRAINT phone_not_null
  CHECK (phone IS NOT NULL) NOT VALID;  -- doesn't scan existing rows
ALTER TABLE users VALIDATE CONSTRAINT phone_not_null;  -- validates without exclusive lock

Migration Tools

Golden Rules

⚠️ Migration Rules
Key Takeaways