How do you perform zero-downtime schema migrations in PostgreSQL?

Answer

Zero-downtime migrations require careful ordering to avoid table locks. Safe operations: adding a nullable column (instant in Postgres 11+ with a default), adding an index (CONCURRENTLY), adding a not-exists constraint. Dangerous operations requiring workarounds: (1) Add NOT NULL column: add nullable → backfill → add constraint as NOT VALID → VALIDATE CONSTRAINT (only takes ShareUpdateExclusiveLock). (2) Add foreign key: ADD CONSTRAINT ... NOT VALID then VALIDATE CONSTRAINT. (3) Reindex: REINDEX CONCURRENTLY. (4) Change column type: add new column → backfill → update trigger → cut over → drop old column. (5) Rename column: use a view or rename-then-add-alias approach. Tools: squawk (CI linter), pgroll, and frameworks like strong_migrations (Rails gem) enforce safe patterns.