A zero-downtime database migration is less about running one perfect command than coordinating compatible schema changes, application releases, data movement, monitoring, and rollback decisions. This reusable checklist shows how to change a production database while keeping a cloud app available, and what to review before every significant migration.
Overview
Production database changes become risky when the application and database stop agreeing about the shape or meaning of data. A release that expects a new column before that column exists can fail. A destructive migration that removes a column while an older application instance still reads it can create errors during a rolling deployment. A large backfill can consume database resources and affect normal requests even when the schema change itself is quick.
The safest general pattern is expand and contract. First, expand the database so it can support both the old and new application versions. Next, deploy application code that uses the expanded schema gradually or behind a feature flag. Then migrate or backfill data, validate the result, and only later contract the schema by removing obsolete columns, constraints, indexes, or tables.
This approach does not guarantee zero interruption. Managed database services, locks, replication behavior, ORM migration tools, and workload patterns vary. Treat zero downtime as an operational objective that requires testing, observability, and a clearly defined abort plan.
What to track
1. The migration inventory
Record the exact objects and operations involved before opening a pull request or change ticket. Include tables, columns, indexes, constraints, views, triggers, stored procedures, data transformations, and application code that reads or writes the affected data. Note whether each operation is additive, potentially blocking, data-intensive, or destructive.
For each item, document the expected duration, required permissions, affected services, and whether it can be interrupted safely. “Add a field” may be low risk in one database and expensive in another if it requires a table rewrite or a validation scan.
2. Compatibility between application versions
Check every version that may run during deployment, including old instances waiting to be replaced, background workers, scheduled jobs, administrative scripts, and mobile clients that may update slowly. The database should remain compatible with both the current and incoming application versions during the transition.
For a new required value, consider this sequence:
- Add the column as nullable or provide a safe default without immediately enforcing the final rule.
- Deploy code that writes the new value while continuing to support existing rows.
- Backfill older records in controlled batches.
- Verify that no relevant rows remain incomplete.
- Enforce the final constraint in a separate change.
3. Backups, replicas, and recovery evidence
Confirm that a recent backup exists and that the retention period covers the migration window. A backup is not the same as a tested recovery. Where the risk justifies it, rehearse restoration or verify the documented recovery procedure in a non-production environment. Check replication lag, replica health, available storage, and the consequences of restoring or promoting a replica.
Do not assume a rollback means reversing the migration. A destructive change or data transformation may not be reversible once committed. Record whether rollback means reverting application code, stopping a backfill, restoring data, or moving forward with a corrective migration.
4. Operational signals
Capture a baseline before the change. Useful signals include request error rate, latency by endpoint, database CPU and memory, connection pool usage, storage growth, lock waits, deadlocks, query duration, transaction volume, replication lag, and background job delay. Add migration-specific progress and error metrics where possible.
Set thresholds that lead to action rather than merely collecting dashboards. For example, define when to pause a backfill, reduce its batch size, disable a feature flag, or stop the deployment. The appropriate threshold depends on the application’s normal range and service objectives.
Cadence and checkpoints
Use the following workflow for each production schema change. Keep the checklist with the migration record so another engineer can understand what happened and why.
Before implementation
- Write the compatibility plan for old and new application versions.
- Classify each database operation by lock risk, duration, and reversibility.
- Test the migration against a representative dataset, not only an empty development database.
- Measure the affected queries before and after the proposed index or schema change.
- Confirm backup status, recovery ownership, maintenance windows, and available capacity.
- Prepare feature flags, pause controls, dashboards, alerts, and a communication path.
During the expand phase
Prefer additive changes first: new nullable columns, new tables, or compatible indexes. Avoid combining expansion with deletion or a large transformation unless the platform and workload have been tested carefully. Apply changes during a period with appropriate operational coverage, but do not rely on a quiet period to make an unsafe operation safe.
Watch lock acquisition separately from total migration time. A migration can execute quickly after obtaining a lock but still create an outage while waiting for a long-running transaction. If the database supports online or concurrent index operations, verify their limitations rather than assuming they eliminate all resource pressure.
During deployment and backfill
Deploy compatible application code before switching traffic to behavior that requires the new schema. If the change affects a user-visible workflow, release it behind a feature flag so the code path and database behavior can be enabled independently.
Run backfills in small, resumable batches. Prefer a stable key range or another deterministic cursor over repeatedly scanning the whole table. Commit each batch, record progress, and make the job safe to restart. Add pacing or adaptive batch sizing when database load rises. Avoid holding one transaction open for the entire dataset unless the database design and recovery plan explicitly support it.
After validation and contraction
Validate counts, null rates, uniqueness, referential relationships, application behavior, and representative query performance. Compare the results with the migration’s acceptance criteria. Keep the old read or write path available until the new path has been observed under real traffic for an appropriate period.
Only then remove obsolete structures. Treat dropping a column, table, or index as its own release. Search application code, reporting jobs, data exports, and operational scripts for remaining references. After contraction, retain the migration record and relevant metrics so a later incident can be traced to the change.
How to interpret changes
A migration’s duration is only one measure of risk. A fast operation that causes lock waits may be more harmful than a slower backfill that yields between batches. Interpret signals together:
- Rising lock waits with normal CPU: investigate blocking transactions, lock scope, and deployment order before continuing.
- Higher latency and growing connection usage: reduce migration pressure or batch size; the application may be competing for database capacity.
- Replication lag increasing: slow writes, reduce backfill concurrency, and confirm that read replicas remain suitable for application traffic.
- Stable infrastructure but rising application errors: inspect compatibility assumptions, serialization, validation, and feature-flag targeting.
- Backfill progress without data-quality improvement: verify the selection query, transformation logic, and whether records are being skipped or repeatedly processed.
Use a stop condition when the observed impact exceeds the approved tolerance. Pausing is usually preferable to improvising a destructive rollback. If the change cannot be safely reversed, freeze further steps, preserve logs and metrics, and choose the least harmful forward fix with the owner of the service and database.
When to revisit
Review this checklist before every production schema change, large backfill, database engine or ORM upgrade, traffic increase, major launch, or change in hosting architecture. A quarterly review is a practical minimum for teams with frequent releases; teams with heavy migration activity may review the runbook monthly.
Refresh the plan when managed database capabilities, migration-tool behavior, backup features, connection limits, or deployment workflows change. Also revisit it after an incident, a failed migration rehearsal, unexpected replication lag, or a change to the application’s availability requirements.
Before the next migration, turn the guidance into a short change record:
- Describe the old and new schema and the compatibility window.
- List the expand, deploy, backfill, validate, and contract steps.
- Attach baseline metrics and define pause thresholds.
- Name the person authorized to stop the change and the person responsible for recovery.
- Record the final validation results and any follow-up cleanup.
For related release preparation, pair this database checklist with a cloud app rollback runbook, an application monitoring plan, and a cloud app capacity worksheet. The goal is not to eliminate every risk; it is to make each production change observable, compatible, interruptible where possible, and understandable when the team needs to act quickly.