The migration failed the moment the wrong column type hit production. Logs lit up. Queries stalled. The schema change was small—just a new column—but it was enough to break everything downstream.
Adding a new column should never feel risky. Yet in many codebases, schema changes can ripple out into cascading failures. A single update can rewrite assumptions baked into queries, indexes, and application logic. Handling them safely means controlling how the column is added, populated, and deployed.
First, define the new column in a way that does not block existing reads and writes. For relational databases, use non-locking DDL when possible. Avoid default values on large tables during the initial add—they can lock rows and impact performance. Instead, create the column as nullable, apply defaults in application logic, and backfill data in controlled batches.
Second, deploy application code that writes to both old and new columns. This ensures compatibility as both schema versions exist in the wild. Once writes are stable, update read logic to consume the new data path. Only after this step should you drop old columns or constraints.