Skip to content
Latchkey

Safe Database Migrations in a Pipeline

Schema changes are the riskiest part of a deploy because old and new code run at once.

During any zero-downtime deploy, old and new application versions briefly share the database. A migration that the old code cannot tolerate causes outages. This lesson teaches the expand-and-contract pattern and how to sequence migrations safely in a pipeline.

Why migrations break deploys

In a rolling or blue-green deploy, both versions hit the same schema. If you rename a column, the version that does not know the new name breaks. The fix is to never make a single breaking change - make a sequence of backward-compatible ones.

Expand and contract

  • Expand: add the new column or table; keep the old one. Both versions work.
  • Migrate: backfill data and dual-write so old and new stay in sync.
  • Switch: deploy code that reads and writes the new shape.
  • Contract: once nothing uses the old shape, drop it in a later release.

Run migrations as a pipeline step

Apply the expand migration before deploying new code, and the contract migration only after the new code is fully rolled out and stable:

.github/workflows/deploy.yml
jobs:
  migrate-expand:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm run migrate:up    # additive, backward-compatible only
  deploy:
    needs: migrate-expand
    runs-on: ubuntu-latest
    steps:
      - run: ./deploy.sh

Guard the destructive steps

Never drop columns or tables in the same release that stops using them. Hold destructive migrations one or more releases behind, so you can roll the application back without hitting a missing column. Keep migrations forward-only and tested against a production-like dataset.

Key takeaways

  • Old and new code share the database mid-deploy, so changes must be backward compatible.
  • Expand-and-contract splits a breaking change into safe additive steps.
  • Delay destructive migrations a release behind so rollback stays possible.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →