Never make a manual, ad-hoc schema change directly against any shared environment (staging, production) — every change goes through a migration file, checked into version control, applied the same way in every environment.
Every migration file is immutable once merged/deployed — to fix a mistake, write a new migration that corrects it, never edit a migration that's already been applied anywhere real.
Backward compatibility during rollout
Assume the old and new application code can both be running against the database simultaneously during a deploy (rolling deploys, multiple instances mid-rollout) — a migration that breaks the currently-running old code before the new code deploys causes an outage during the deploy window itself, not after.
Add a new nullable column, then backfill, then add NOT NULL in a later migration — never add a NOT NULL column with no default directly to a table with existing rows in one step; it either fails outright or locks the table for the backfill duration.
When renaming a column, do it as: add new column → dual-write in application code → backfill → switch reads → remove old column, across multiple deploys — never a single-step rename that old code can't tolerate mid-deploy.
Destructive changes
Never drop a column or table in the same migration that stops using it — deploy the code change that stops using it first, confirm it's live and stable, then drop the now-unused column/table in a follow-up migration.
Treat every DROP, TRUNCATE, and irreversible data transformation as requiring a second reviewer's explicit sign-off before it runs against production data.
Locking and large tables
Be aware that some schema operations take a table-level lock for their duration (adding certain constraints, some column additions on older engine versions) — on a large, high-traffic table this can cause a visible outage; use the engine's documented online/concurrent variants where available.
Run migrations against a realistic data volume in staging before assuming a migration is "quick" — a migration that's instant on a 500-row dev table can lock a 50-million-row production table for minutes.
Reversibility and testing
Write a rollback (down) migration alongside every forward migration where the engine/tooling supports it, and actually test the rollback path, not just the forward path.
Run every migration against a copy of production-shaped data (or a realistic seed) in CI before it's mergeable, not just against an empty local database.
Comments (0)
Sign in to comment. Sign in
No comments yet. Be the first to add one.