Adding a new column sounds simple. It isn’t, if you need zero downtime, predictable deployments, and clean rollbacks. Schema changes can break production faster than bad code if you don’t plan the step sequence.
First, define the new column in a non-breaking way. Avoid NOT NULL without a default. Use nullable fields or safe defaults so the migration can run without locking large tables. This keeps reads and writes flowing during the change.
Second, deploy in phases. Add the new column in one migration. Backfill data in batches using an async job or background worker. Only after the backfill completes should you enforce constraints or switch application logic to read from the new column.
Third, test the migration scripts against production-like data sets. Synthetic dev data hides slow queries and lock contention that appear when adding a new column to billions of rows.