Adding a new column is more than a schema update. It changes the shape of your data, the assumptions in your queries, and the behavior of your application. Treat it as a change in contract. Every migration should be explicit, reversible, and tested.
First, define the new column with the exact type and constraints required. Choose NOT NULL only if you can backfill every row. Use defaults with intention—mind that they write to all existing rows and may lock the table on large datasets.
Next, update related code paths in your ORM, query builders, and raw SQL. A column that’s unused in code is a liability. Integrate it into relevant SELECT statements to keep query plans current and prevent regressions.
Run migrations in a staging environment that mirrors production. For large tables, consider adding the column as nullable, backfilling in small batches, and then applying the constraint. This reduces write locks and avoids downtime.