Running Database Migrations Safely in Your Pipeline
A schema change is a deploy too, and the riskiest one to get wrong.
Code can roll back in seconds; a dropped column cannot. Database migrations are the part of a deploy with the least reversibility, so they demand a careful pattern. This lesson covers the expand-and-contract approach, ordering migrations against deploys, and keeping changes backward compatible.
Why migrations are special
A deploy can be rolled back by redeploying the previous image. A migration that drops or renames a column is destructive and not trivially reversible. The safe strategy is to make every schema change backward compatible with the currently running code, so old and new versions can coexist during a rollout.
Expand and contract
- Expand: add the new column or table without removing anything. Old code ignores it; new code can use it.
- Migrate: backfill data and switch the application to read and write the new shape.
- Contract: only after the old code is fully gone, drop the obsolete column in a later, separate deploy.
Ordering migrations and deploys
For additive (expand) migrations, run the migration before deploying code that depends on it. For destructive (contract) changes, run them after the dependent code is fully deployed and stable. Never bundle an expand and a contract for the same column into one release.
A migration step in the pipeline
Run migrations as a discrete, gated step before the application deploy. Keep them idempotent and forward-only so a re-run is safe.
jobs:
migrate:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- run: npm run migrate:deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
deploy:
needs: migrate
runs-on: ubuntu-latest
steps:
- run: ./deploy.shKey takeaways
- Make schema changes backward compatible so old and new code can coexist during a rollout.
- Expand (add) before deploying dependent code; contract (drop) only after old code is gone.
- Run migrations as a discrete, idempotent, forward-only step gated before the deploy.