Zero-Downtime Database Migrations: Expand-Contract Playbook
Schema changes are one of the most common sources of deployment incidents. The risky pattern is changing schema and application assumptions in the same release window.
The safer pattern is expand-contract.
Expand phase
Add new columns/tables/indexes in a backward-compatible way. Keep old reads and writes working.
Dual-write and backfill
Temporarily write to old and new schema paths. Run backfill in controlled batches with progress checkpoints.
Read switch
Move reads to the new schema behind a feature flag. Validate latency, error rates, and data consistency before full rollout.
Contract phase
After traffic stabilizes and old path usage drops to zero, remove old columns and code paths in a separate release.
Operational checklist
- Avoid table-wide locks on hot paths.
- Use concurrent index creation where supported.
- Add migration observability (duration, rows/sec, error count).
- Prepare rollback strategy for each phase.
Conclusion
Zero-downtime migrations are not a single SQL script; they are a release sequence. Expand-contract gives teams a repeatable method to evolve schemas without breaking running traffic.
Related posts
Feature Flag Architecture: Safe Releases with OpenFeature
How to decouple deployment from release, run controlled rollouts, and keep vendor flexibility with OpenFeature.
PostgreSQL Connection Pooling: PgBouncer Setup with Go and Node.js
Manage DB connections in production: PgBouncer transaction pooling, Go database/sql settings, and Node.js pg pool tuning.
Integration Testing in Go: Mocking, Databases, Queues, and External Calls — Advanced Guide
Layered integration testing with sqlmock, Testcontainers, httptest, interface-based fake brokers, and external API mocks — architecture diagrams and production-style Go examples.