Data & Storage
Estimated time: 20–35 min

Database Migration Checklist

A comprehensive, stack-agnostic checklist to safely plan, review, execute, backfill, and recover from production database schema and data migrations.

#Database#PostgreSQL#MySQL#SQL Server#Migrations#Zero Downtime#Data Safety#Table Locks#Backfills
Migration Scope Type:
Phase Preset:
Progress:0%0 of 54 completed

1. Migration Scope & Impact Assessment

Understand the target table size, expected row counts, and application dependencies.

0/5 checks
Clear business & technical justification documentedBlockingBackend Dev
Target database engine & version verifiedBlockingDBA
Target table row count & disk size auditedBlockingDBA / Dev
Application dependencies & active writers mappedBlockingBackend Dev
Expected DDL execution duration & window scheduledImportantDevOps / DBA

2. Strict Backward Compatibility & Expand-Contract

Ensure old and new application versions work seamlessly during zero-downtime deployments.

0/5 checks
Multi-phase Expand-Contract strategy appliedBlockingTech Lead
New columns added as optional (NULL) or with defaultsBlockingBackend Dev
Column renaming uses safe dual-write or view strategyBlockingBackend Dev
Data type changes split into additive column migrationBlockingBackend Dev
Previous application version binary tested against new schemaBlockingQA / Dev

3. Table Locks, Availability & Timeout Semantics

Control lock acquisition times and statement timeouts to prevent production query blocking.

0/5 checks
Lock Timeout explicitly configured in DDL scriptBlockingDBA
Table lock mode & concurrency impact evaluatedBlockingDBA
Full table rewrite requirements auditedBlockingDBA
DDL transaction duration minimizedBlockingBackend Dev
Statement Timeout configured for long queriesImportantDBA

4. Data Integrity, Constraints & Dirty Data

Validate existing records before applying constraints to prevent lockups or failed migrations.

0/5 checks
Existing data audited for constraint violationsBlockingBackend Dev
Foreign Key constraints added as NOT VALID firstBlockingDBA
NOT NULL constraints added safely via CHECK constraintImportantDBA
Unique constraints backed by non-blocking indexImportantDBA
Engine transactional DDL behavior verifiedImportantBackend Dev

5. Asynchronous Data Migration & Chunked Backfills

Execute large data transformations safely without replication lag or CPU spikes.

0/6 checks
Backfill scripts batched in small chunksBlockingBackend Dev
Indexed Primary Key range filtering used (No OFFSET)BlockingBackend Dev
Backfill job made strictly idempotentBlockingBackend Dev
Replication lag monitored & auto-throttledBlockingDevOps / DBA
Progress tracking & crash resume state persistedImportantBackend Dev
Dual-writing application logic active during backfillBlockingBackend Dev

6. Index Strategy, Query Planner & Build Method

Build indexes online without blocking write traffic or overwhelming buffer pool memory.

0/5 checks
Non-blocking index build method specifiedBlockingDBA
Index size & buffer cache overhead evaluatedImportantDBA
Duplicate & redundant indexes auditedSuggestionDBA
Query planner execution plan (EXPLAIN ANALYZE) inspectedBlockingBackend Dev
Write throughput impact on INSERT/UPDATE evaluatedImportantDBA

7. Rollback & Irreversible Recovery Strategy

Distinguish between safe reversible DDL and destructive irreversible operations.

0/5 checks
Irreversible operations clearly flaggedBlockingTech Lead
Point-in-time recovery (PITR) & WAL backup activeBlockingDevOps / DBA
Safe down-migration script verified for non-destructive DDLImportantDBA / Dev
Lock acquisition failure retry contingencyImportantDBA
Application rollback compatibility confirmedBlockingTech Lead

8. Realistic Staging & Volume Testing

Test migration lock durations and performance against production-scale datasets.

0/4 checks
Migration executed on production-scale staging DBBlockingQA / DBA
Lock acquisition wait & DDL duration measuredImportantDBA
Automated application integration test suite executedBlockingQA
Backfill job load-tested at 10x scaleImportantBackend Dev

9. Deployment & Execution Ordering

Establish exact deployment sequence, responsible leads, and stop conditions.

0/4 checks
Deployment sequence steps strictly documentedBlockingTech Lead
Designated DBA / Backend engineer executing DDLBlockingRelease Lead
Explicit Stop & Abort conditions definedBlockingDBA
Off-peak maintenance window scheduled for high-risk DDLImportantDevOps

10. Post-Migration Live Verification

Verify schema state, row counts, query latency, and database replication health.

0/5 checks
Live database schema state inspectedBlockingDBA
Data checksum & row count verification passedBlockingBackend Dev
Database CPU, connection pool & query latency normalBlockingDevOps
Production app logs inspected for SQL errorsBlockingBackend Dev
Read-replica lag returned to zeroImportantDBA

11. Multi-Stage Cleanup & Artifact Removal

Decommission legacy columns, dual-write shims, and temporary backfill code in future releases.

0/5 checks
Application updated to read solely from new schemaImportantBackend Dev
Legacy dual-writing application code removedImportantBackend Dev
Obsolete columns & tables dropped in separate maintenance DDLImportantDBA / Dev
Temporary backfill scripts & background jobs decommissionedNitBackend Dev
Temporary feature flags & compatibility shims cleaned upNitBackend Dev
High-Risk DDL Danger Matrix

Dangerous Database Operations & Mitigation Strategies

Certain DDL statements trigger exclusive table locks or full table rewrites that can bring down production services. Use safe multi-phase alternatives:

1. Dropping or Renaming Columns in Active Use

Executing ALTER TABLE DROP COLUMN or RENAME COLUMN while older application instances are running causes immediate 500 query errors. Use the Expand-Contract pattern instead.

2. Adding NOT NULL Constraints to Populated Tables

Adding NOT NULL requires scanning every row in the table, acquiring an exclusive table lock. On Postgres, use CHECK (col IS NOT NULL) NOT VALID and validate asynchronously.

3. Synchronous Index Creation on Large Tables

Standard CREATE INDEX blocks all write queries on the table for the entire duration of the build. Always use non-blocking builds: CONCURRENTLY (Postgres) or ONLINE=ON (SQL Server).

4. In-Place Column Data Type Alterations

Executing ALTER COLUMN TYPE rewrites the entire physical table file on disk. Instead, create a new column with the target type, dual-write in application code, backfill data, and switch reads.

Zero-Downtime Pattern

The 4-Phase Expand-Contract Migration Lifecycle

To achieve zero-downtime database changes, decouple schema DDL deployments from application code releases across four distinct phases:

1Expand Schema

Add new optional/nullable columns or tables. Old application servers continue reading and writing to old schema without error.

2Dual-Write App

Deploy application code that writes new incoming data to both old and new columns, while continuing to read from old schema.

3Async Backfill

Run batched background scripts to populate historical records into new columns in small primary-key ranges (500–2,000 rows).

4Contract Schema

Switch application reads to new column. In a separate follow-up deployment, drop the old legacy column.

Engine Specifics

Engine-Specific Table Locking Pitfalls

Database engines handle DDL locking and online schema changes differently. Always configure lock timeouts for your specific engine:

PostgreSQLTransactional DDL

Postgres supports DDL inside transactions. Always configure SET lock_timeout = '5s'; before DDL so blocked queries do not stack up behind lock requests.

CREATE INDEX CONCURRENTLY idx ON table(col);
MySQL / InnoDBAuto-Committing DDL

MySQL DDL statements auto-commit implicit transactions. Use ALGORITHM=INPLACE, LOCK=NONE or tools like gh-ost / pt-online-schema-change for large tables.

ALTER TABLE t ADD INDEX idx(col), ALGORITHM=INPLACE, LOCK=NONE;
SQL ServerOnline Operations

Use ONLINE = ON for non-blocking index creation and rebuilds. Monitor tempdb allocation and transaction log size during online index operations.

CREATE INDEX idx ON table(col) WITH (ONLINE = ON);

When to Use This Checklist

  • Before running DDL schema migrations or DML data transformations on production databases.
  • When adding, altering, or dropping columns, tables, foreign keys, or unique constraints.
  • During asynchronous data backfills, table refactoring, or zero-downtime expand-contract deployments.
  • To evaluate exclusive table locks, statement timeouts, and point-in-time recovery readiness.

Common Pitfalls to Avoid

  • Adding NOT NULL constraints or non-constant DEFAULT values on populated tables, causing extended exclusive table locks.
  • Creating indexes synchronously without CONCURRENTLY (Postgres) or ONLINE=ON (SQL Server), blocking all write traffic.
  • Dropping or renaming active columns in the same release as application updates, breaking older running app instances.
  • Executing un-chunked UPDATE scripts that exhaust transaction logs, cause high replication lag, or lock rows for minutes.
  • Relying on automatic DOWN migrations for destructive DDL without verifying point-in-time backups.