Principles:
- Prefer backward-compatible migrations (expand → migrate → contract).
- Minimize locking and long transactions; design for online changes where feasible.
- Data backfills must be idempotent, resumable, and observable.
Checklist:
- Plan
- Identify tables/columns/indexes affected
- Determine rollout strategy:
- Expand: add nullable column / new table / additive index
- Migrate: backfill and dual-write/read if needed
- Contract: drop old column, enforce constraints, finalize
- Define rollback approach and what "safe to retry" means
- Migration implementation
- Create migration (EF Core / SQL tool)
- Ensure indexes/constraints are appropriate and named consistently
- Check for default values and nullability
- Evaluate lock risk (large table rewrite, index build)
- Data backfill (if required)
- Write an idempotent backfill job/script:
- batches with stable ordering
- progress logging / metrics
- safe retry without corruption
- can be paused/resumed
- Verify performance impact (throttling, time windows)
- App changes
- Ensure app supports both old and new schema during rollout
- Update queries/projections to avoid breaking older deployments (if relevant)
- Verification
- Run migration locally in a realistic dataset if possible
- Add/update tests (integration tests if repo supports them)
- Document runbook for prod (timing, expected duration, monitoring)
Finish with:
- Migration plan (expand/migrate/contract steps)
- Commands run + results
- Operational notes (runtime, monitoring, rollback)
- Risks/follow-ups