Post

Handling DB Migrations in CI/CD Safely

Introduction

Database migrations are the highest-risk part of deployment because they can permanently alter state. Safe automation requires backward-compatible changes, validation, and explicit rollout sequencing.

Adopt the Expand and Contract Pattern

The safest strategy is to expand the schema first, deploy application changes, then contract old fields later.

Workflow:

  1. Add new columns or tables without removing old ones.
  2. Deploy application code that writes to both old and new fields.
  3. Backfill data.
  4. Remove old fields after validating usage.

Migration Automation in CI/CD

Migrations should be executed as part of deployment, not manually. For EF Core, a migration can be applied via a dedicated migration job.

1
2
3
4
using Microsoft.EntityFrameworkCore;

await using var db = new OrdersDbContext();
await db.Database.MigrateAsync();

Run this in a migration step with strong RBAC and a short timeout to avoid long-running locks.

Pre-Deployment Validation

Use dry-run checks before applying migrations:

  • Validate migration scripts in a staging environment.
  • Detect destructive operations and require explicit approval.
  • Confirm that the migration is backward compatible with the currently running code.

Data Backfills

Large backfills should run asynchronously. Use background jobs with throttling and checkpoints to avoid long locks.

Rollback Strategy

Schema rollbacks are harder than application rollbacks. Ensure old code can run against the new schema until full validation is done. Avoid dropping columns until the system is stable.

Summary

Safe database migrations depend on backward compatibility, automation, and deliberate sequencing. Treat migrations as first-class deployment steps, and apply them with the same rigor as application releases.

This post is licensed under CC BY 4.0 by the author.