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:
- Add new columns or tables without removing old ones.
- Deploy application code that writes to both old and new fields.
- Backfill data.
- 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.