The migration broke at 2:14 a.m. A missing new column in the database stopped the deploy cold.
A new column sounds simple: add a field, update the schema, ship the change. But the reality is a chain of dependencies that will fail if not planned. Code must change. Migrations must run. Data backfills must complete without blocking requests. Every layer, from the database to the API, must stay in sync.
When adding a new column, start with explicit schema changes. Define the column type, constraints, and defaults. Avoid implicit casting by the database engine. For large datasets, use an additive migration pattern: create the column without constraints first, populate it in batches, then enforce constraints in a second step. This prevents locks and downtime.
In the application layer, implement reads from the new column before writes. Deploy these reads first to ensure the field exists and can be handled gracefully. Then roll out writes in a separate deployment. This two-step rollout allows for quick rollback if the application encounters unexpected data or nulls.