Data Migration and ETL Patterns
1. Zero-Downtime Migration Principles
Core Rules
- Never perform destructive schema changes in a single step
- Always maintain backward compatibility during the transition period
- Deploy application changes before (or simultaneously with) schema changes
- Validate data integrity at every phase before proceeding to the next
- Have a tested rollback plan before starting any migration
Migration Strategy Selection
| Strategy |
Complexity |
Best For |
Risk Level |
| Expand-and-Contract |
Medium |
Column renames, type changes |
Low |
| Dual-Write |
High |
Table splits, system migrations |
Medium |
| Shadow Table |
Medium |
Large table restructuring |
Low |
| Blue-Green Database |
Very High |
Full database engine replacement |
High |
Risk Assessment Before Migration
| Factor |
Low Risk |
High Risk |
| Table size |
< 1M rows |
> 100M rows |
| Write frequency |
< 100 writes/sec |
> 1000 writes/sec |
| Downtime tolerance |
Minutes acceptable |
Zero tolerance |
| Rollback complexity |
Simple reverse migration |
Requires data reconciliation |
| Dependencies |
Single application |
Multiple services share the table |
2. Expand-and-Contract Pattern (Parallel Change)
A three-phase approach to safely evolve database schemas.
Phase Overview
Phase 1 — Expand: Add new column/table alongside the old one
Phase 2 — Migrate: Backfill data, update application to use new structure
Phase 3 — Contract: Remove old column/table after verification
Example: Renaming a Column
-- Phase 1: Expand — add new column
ALTER TABLE users ADD COLUMN full_name VARCHAR(255);
-- Phase 2: Migrate — backfill data
UPDATE users SET full_name = name WHERE full_name IS NULL;
-- Application deploys: write to both columns, read from full_name
-- Phase 3: Contract — remove old column (after verification period)
ALTER TABLE users DROP COLUMN name;
Expand-and-Contract Execution Rules
- Each phase must be a separate deployment
- Allow a stabilization period between phases (at least one full release cycle)
- Monitor error rates and query performance between phases
- Never skip the verification step before the contract phase
3. Dual-Write Pattern
Four Stages
Stage 1 — Dark Write: Write to new system, but do not read from it
Stage 2 — Shadow Read: Read from both systems, compare results, serve old
Stage 3 — Cutover: Read from new system, stop writing to old
Stage 4 — Cleanup: Remove old system references
Implementation Guidelines
- The old system remains the source of truth until Stage 3
- Log all discrepancies found during Shadow Read without failing requests
- Set a discrepancy threshold (e.g., < 0.01%) before allowing cutover
- Implement circuit breakers to fall back to the old system if the new system fails
Consistency Considerations
- Dual writes are not atomic — handle partial failures explicitly
- Use idempotent writes to safely retry on failure
- Consider using an outbox pattern or CDC to keep systems in sync
- Monitor write lag between systems during the transition
For detailed implementation guides, see references/zero-downtime-patterns.md.
4. Shadow Table Pattern
Process
1. Create new table with desired schema
2. Set up triggers or CDC to replicate ongoing changes
3. Backfill historical data
4. Validate data consistency
5. Swap table names atomically (RENAME TABLE)
6. Drop old table after verification period
Shadow Table Execution Rules
- Trigger-based replication adds overhead to every write — monitor performance
- Backfill in batches to avoid long-running transactions
- During the swap, briefly acquire an exclusive lock — plan for this
- Keep the old table for a defined retention period before dropping
5. Online Schema Migration Tools
| Feature |
gh-ost |
pt-osc |
pgroll |
| Database |
MySQL |
MySQL |
PostgreSQL |
| Mechanism |
Binlog streaming |
Triggers |
Version-based schema |
| Lock-free |
Yes |
Mostly |
Yes |
| Throttle support |
Yes (built-in) |
Yes (built-in) |
N/A |
| FK support |
Limited |
Yes |
Yes |
| Replication-friendly |
Yes |
Requires caution |
N/A |
Tool Selection Guide
- gh-ost: Preferred for MySQL when replication lag sensitivity is critical
- pt-online-schema-change: Use when foreign key support is required on MySQL
- pgroll: Use for PostgreSQL version-based schema migrations with rollback
For detailed tool comparison and usage, see references/online-schema-tools.md.
6. Backward-Compatible Schema Changes
Safe Changes (Additive-Only)
| Operation |
Safe |
Notes |
| Add nullable column |
Yes |
No existing code breaks |
| Add table |
Yes |
No existing code breaks |
| Add index |
Yes |
Use CONCURRENTLY on large tables (PG) |
| Add column with default |
Yes |
Safe on modern MySQL 8.0+ and PostgreSQL |
| Widen column type |
Yes |
e.g., VARCHAR(50) to VARCHAR(100) |
Unsafe Changes (Require Expand-and-Contract)
| Operation |
Risk |
Required Approach |
| Drop column |
High |
Remove app references first, then drop |
| Rename column |
High |
Add new, migrate, then drop old |
| Narrow column type |
High |
May truncate data |
| Change column to NOT NULL |
Medium |
Backfill defaults first, then add constraint |
| Drop table |
High |
Verify no references, keep backup |
Schema Change Compatibility Rules
- All schema changes in production must be additive-only in the first deployment
- Destructive changes happen in subsequent deployments after verification
- Every migration must be reviewed for backward compatibility before merge
7. Data Validation and Reconciliation
Validation Stages
Pre-migration: Snapshot source data counts and checksums
During: Monitor migration progress and error rates
Post-migration: Compare source vs. target data
Reconciliation Checklist
- Row count comparison between source and target
- Checksum or hash comparison on critical columns
- Referential integrity verification on the target
- Boundary value and edge case spot checks
- Application-level smoke tests against the new data
Automated Validation Query Example
-- Row count comparison
SELECT 'source' AS system, COUNT(*) AS row_count FROM old_table
UNION ALL
SELECT 'target' AS system, COUNT(*) AS row_count FROM new_table;
-- Checksum comparison on critical columns
SELECT MD5(GROUP_CONCAT(CONCAT(id, email, status) ORDER BY id)) AS checksum
FROM old_table;
SELECT MD5(GROUP_CONCAT(CONCAT(id, email, status) ORDER BY id)) AS checksum
FROM new_table;
Validation Acceptance Rules
- Never skip post-migration validation, even for small migrations
- Define acceptance criteria (e.g., 100% row match, checksum match) before starting
- Keep source data accessible for at least one release cycle after migration
8. ETL vs. ELT Pattern Selection
Comparison
| Aspect |
ETL |
ELT |
| Transform location |
Staging/middleware |
Target system (data warehouse) |
| Best for |
Structured, well-defined schemas |
Exploratory analytics, raw storage |
| Latency |
Higher (transform before load) |
Lower (load first, transform later) |
| Scalability |
Limited by middleware compute |
Leverages target system compute |
| Data quality |
Enforced before loading |
Enforced after loading |
Selection Guide
- Use ETL when data quality must be guaranteed before loading
- Use ELT when the target system has strong compute capability (e.g., BigQuery, Snowflake)
- Use ETL for compliance-sensitive data that must be masked before storage
- Use ELT when schema is evolving and raw data preservation is important
Pipeline Design Guidelines
- Make every pipeline step idempotent and re-runnable
- Use watermark or checkpoint-based incremental loading
- Implement dead-letter queues for records that fail transformation
- Log and alert on data quality metric degradation
- Version pipeline configurations alongside application code
9. CDC (Change Data Capture)
Pattern Overview
Source DB → Change Log (binlog/WAL) → CDC Connector → Target System
CDC Approaches
| Approach |
Mechanism |
Latency |
Impact on Source |
| Log-based |
Read DB transaction log |
Near real-time |
Minimal |
| Trigger-based |
Database triggers |
Real-time |
Moderate |
| Poll-based |
Periodic SELECT queries |
Seconds-minutes |
Low-Moderate |
| Timestamp-based |
Query by updated_at column |
Seconds-minutes |
Low |
CDC Implementation Rules
- Prefer log-based CDC (e.g., Debezium) for minimal source impact
- Ensure CDC consumers handle out-of-order and duplicate events
- Include schema change handling in CDC pipeline design
- Monitor CDC lag — alert if lag exceeds acceptable thresholds
- Handle DDL changes in the CDC stream (add column, drop column)
Common Tools
| Tool |
Source Support |
Sink Support |
| Debezium |
MySQL, PG, MongoDB |
Kafka, any Kafka consumer |
| AWS DMS |
Most RDBMS, MongoDB |
S3, RDS, Redshift, etc. |
| Airbyte |
300+ connectors |
Data warehouses, lakes |
| Fivetran |
SaaS, RDBMS |
Data warehouses |
10. Rollback Strategies
Rollback Approach by Migration Type
| Migration Type |
Rollback Strategy |
| Add column |
Drop column (safe if no code references it yet) |
| Rename column (expanded) |
Revert app to read old column, drop new column |
| Data backfill |
No rollback needed if old column is still populated |
| Table split (dual-write) |
Revert to old system reads, stop new system writes |
| System migration |
Circuit breaker fallback to old system |
Rollback Execution Rules
- Every migration must have a documented rollback procedure
- Test rollback procedures in staging before production deployment
- Set a point-of-no-return threshold — define when rollback is no longer safe
- Maintain data backups taken before migration for at least one release cycle
- Rollback must not cause data loss for writes that occurred during migration
11. Large-Scale Migration Checklist
Pre-Migration
During Migration
Post-Migration
Additional References
- For zero-downtime pattern details, see references/zero-downtime-patterns.md
- For online schema tool comparison, see references/online-schema-tools.md
1---2name: data-migration3description: Data migration and ETL patterns including zero-downtime migration strategies (dual-write, shadow table, expand-and-contract), online schema migration tools (gh-ost, pt-online-schema-change, pgroll), large-scale data migration planning, ETL/ELT pipeline design, CDC (Change Data Capture), backward compatible schema changes, data validation and reconciliation, and rollback strategies. Use when planning database migrations, implementing zero-downtime schema changes, designing ETL/ELT pipelines, or performing large-scale data movement between systems.4license: MIT5---67# Data Migration and ETL Patterns89## 1. Zero-Downtime Migration Principles1011### Core Rules1213- Never perform destructive schema changes in a single step14- Always maintain backward compatibility during the transition period15- Deploy application changes before (or simultaneously with) schema changes16- Validate data integrity at every phase before proceeding to the next17- Have a tested rollback plan before starting any migration1819### Migration Strategy Selection2021| Strategy | Complexity | Best For | Risk Level |22| ------------------- | ---------- | -------------------------------- | ---------- |23| Expand-and-Contract | Medium | Column renames, type changes | Low |24| Dual-Write | High | Table splits, system migrations | Medium |25| Shadow Table | Medium | Large table restructuring | Low |26| Blue-Green Database | Very High | Full database engine replacement | High |2728### Risk Assessment Before Migration2930| Factor | Low Risk | High Risk |31| ------------------- | ------------------------ | ----------------------------------- |32| Table size | < 1M rows | > 100M rows |33| Write frequency | < 100 writes/sec | > 1000 writes/sec |34| Downtime tolerance | Minutes acceptable | Zero tolerance |35| Rollback complexity | Simple reverse migration | Requires data reconciliation |36| Dependencies | Single application | Multiple services share the table |3738---3940## 2. Expand-and-Contract Pattern (Parallel Change)4142A three-phase approach to safely evolve database schemas.4344### Phase Overview4546```text47Phase 1 — Expand: Add new column/table alongside the old one48Phase 2 — Migrate: Backfill data, update application to use new structure49Phase 3 — Contract: Remove old column/table after verification50```5152### Example: Renaming a Column5354```sql55-- Phase 1: Expand — add new column56ALTER TABLE users ADD COLUMN full_name VARCHAR(255);5758-- Phase 2: Migrate — backfill data59UPDATE users SET full_name = name WHERE full_name IS NULL;6061-- Application deploys: write to both columns, read from full_name6263-- Phase 3: Contract — remove old column (after verification period)64ALTER TABLE users DROP COLUMN name;65```6667### Expand-and-Contract Execution Rules6869- Each phase must be a separate deployment70- Allow a stabilization period between phases (at least one full release cycle)71- Monitor error rates and query performance between phases72- Never skip the verification step before the contract phase7374---7576## 3. Dual-Write Pattern7778### Four Stages7980```text81Stage 1 — Dark Write: Write to new system, but do not read from it82Stage 2 — Shadow Read: Read from both systems, compare results, serve old83Stage 3 — Cutover: Read from new system, stop writing to old84Stage 4 — Cleanup: Remove old system references85```8687### Implementation Guidelines8889- The old system remains the source of truth until Stage 390- Log all discrepancies found during Shadow Read without failing requests91- Set a discrepancy threshold (e.g., < 0.01%) before allowing cutover92- Implement circuit breakers to fall back to the old system if the new system fails9394### Consistency Considerations9596- Dual writes are not atomic — handle partial failures explicitly97- Use idempotent writes to safely retry on failure98- Consider using an outbox pattern or CDC to keep systems in sync99- Monitor write lag between systems during the transition100101For detailed implementation guides, see [references/zero-downtime-patterns.md](references/zero-downtime-patterns.md).102103---104105## 4. Shadow Table Pattern106107### Process108109```text1101. Create new table with desired schema1112. Set up triggers or CDC to replicate ongoing changes1123. Backfill historical data1134. Validate data consistency1145. Swap table names atomically (RENAME TABLE)1156. Drop old table after verification period116```117118### Shadow Table Execution Rules119120- Trigger-based replication adds overhead to every write — monitor performance121- Backfill in batches to avoid long-running transactions122- During the swap, briefly acquire an exclusive lock — plan for this123- Keep the old table for a defined retention period before dropping124125---126127## 5. Online Schema Migration Tools128129| Feature | gh-ost | pt-osc | pgroll |130| -------------------- | ---------------- | ---------------- | -------------------- |131| Database | MySQL | MySQL | PostgreSQL |132| Mechanism | Binlog streaming | Triggers | Version-based schema |133| Lock-free | Yes | Mostly | Yes |134| Throttle support | Yes (built-in) | Yes (built-in) | N/A |135| FK support | Limited | Yes | Yes |136| Replication-friendly | Yes | Requires caution | N/A |137138### Tool Selection Guide139140- **gh-ost**: Preferred for MySQL when replication lag sensitivity is critical141- **pt-online-schema-change**: Use when foreign key support is required on MySQL142- **pgroll**: Use for PostgreSQL version-based schema migrations with rollback143144For detailed tool comparison and usage, see [references/online-schema-tools.md](references/online-schema-tools.md).145146---147148## 6. Backward-Compatible Schema Changes149150### Safe Changes (Additive-Only)151152| Operation | Safe | Notes |153| ----------------------- | ---- | ---------------------------------------- |154| Add nullable column | Yes | No existing code breaks |155| Add table | Yes | No existing code breaks |156| Add index | Yes | Use `CONCURRENTLY` on large tables (PG) |157| Add column with default | Yes | Safe on modern MySQL 8.0+ and PostgreSQL |158| Widen column type | Yes | e.g., `VARCHAR(50)` to `VARCHAR(100)` |159160### Unsafe Changes (Require Expand-and-Contract)161162| Operation | Risk | Required Approach |163| ------------------------- | ------ | -------------------------------------------- |164| Drop column | High | Remove app references first, then drop |165| Rename column | High | Add new, migrate, then drop old |166| Narrow column type | High | May truncate data |167| Change column to NOT NULL | Medium | Backfill defaults first, then add constraint |168| Drop table | High | Verify no references, keep backup |169170### Schema Change Compatibility Rules171172- All schema changes in production must be additive-only in the first deployment173- Destructive changes happen in subsequent deployments after verification174- Every migration must be reviewed for backward compatibility before merge175176---177178## 7. Data Validation and Reconciliation179180### Validation Stages181182```text183Pre-migration: Snapshot source data counts and checksums184During: Monitor migration progress and error rates185Post-migration: Compare source vs. target data186```187188### Reconciliation Checklist189190- Row count comparison between source and target191- Checksum or hash comparison on critical columns192- Referential integrity verification on the target193- Boundary value and edge case spot checks194- Application-level smoke tests against the new data195196### Automated Validation Query Example197198```sql199-- Row count comparison200SELECT 'source' AS system, COUNT(*) AS row_count FROM old_table201UNION ALL202SELECT 'target' AS system, COUNT(*) AS row_count FROM new_table;203204-- Checksum comparison on critical columns205SELECT MD5(GROUP_CONCAT(CONCAT(id, email, status) ORDER BY id)) AS checksum206FROM old_table;207208SELECT MD5(GROUP_CONCAT(CONCAT(id, email, status) ORDER BY id)) AS checksum209FROM new_table;210```211212### Validation Acceptance Rules213214- Never skip post-migration validation, even for small migrations215- Define acceptance criteria (e.g., 100% row match, checksum match) before starting216- Keep source data accessible for at least one release cycle after migration217218---219220## 8. ETL vs. ELT Pattern Selection221222### Comparison223224| Aspect | ETL | ELT |225| ------------------ | -------------------------------- | ----------------------------------- |226| Transform location | Staging/middleware | Target system (data warehouse) |227| Best for | Structured, well-defined schemas | Exploratory analytics, raw storage |228| Latency | Higher (transform before load) | Lower (load first, transform later) |229| Scalability | Limited by middleware compute | Leverages target system compute |230| Data quality | Enforced before loading | Enforced after loading |231232### Selection Guide233234- Use **ETL** when data quality must be guaranteed before loading235- Use **ELT** when the target system has strong compute capability (e.g., BigQuery, Snowflake)236- Use **ETL** for compliance-sensitive data that must be masked before storage237- Use **ELT** when schema is evolving and raw data preservation is important238239### Pipeline Design Guidelines240241- Make every pipeline step idempotent and re-runnable242- Use watermark or checkpoint-based incremental loading243- Implement dead-letter queues for records that fail transformation244- Log and alert on data quality metric degradation245- Version pipeline configurations alongside application code246247---248249## 9. CDC (Change Data Capture)250251### Pattern Overview252253```text254Source DB → Change Log (binlog/WAL) → CDC Connector → Target System255```256257### CDC Approaches258259| Approach | Mechanism | Latency | Impact on Source |260| --------------- | ---------------------------- | --------------- | ---------------- |261| Log-based | Read DB transaction log | Near real-time | Minimal |262| Trigger-based | Database triggers | Real-time | Moderate |263| Poll-based | Periodic SELECT queries | Seconds-minutes | Low-Moderate |264| Timestamp-based | Query by `updated_at` column | Seconds-minutes | Low |265266### CDC Implementation Rules267268- Prefer log-based CDC (e.g., Debezium) for minimal source impact269- Ensure CDC consumers handle out-of-order and duplicate events270- Include schema change handling in CDC pipeline design271- Monitor CDC lag — alert if lag exceeds acceptable thresholds272- Handle DDL changes in the CDC stream (add column, drop column)273274### Common Tools275276| Tool | Source Support | Sink Support |277| -------- | ------------------ | ------------------------- |278| Debezium | MySQL, PG, MongoDB | Kafka, any Kafka consumer |279| AWS DMS | Most RDBMS, MongoDB| S3, RDS, Redshift, etc. |280| Airbyte | 300+ connectors | Data warehouses, lakes |281| Fivetran | SaaS, RDBMS | Data warehouses |282283---284285## 10. Rollback Strategies286287### Rollback Approach by Migration Type288289| Migration Type | Rollback Strategy |290| ------------------------ | --------------------------------------------------- |291| Add column | Drop column (safe if no code references it yet) |292| Rename column (expanded) | Revert app to read old column, drop new column |293| Data backfill | No rollback needed if old column is still populated |294| Table split (dual-write) | Revert to old system reads, stop new system writes |295| System migration | Circuit breaker fallback to old system |296297### Rollback Execution Rules298299- Every migration must have a documented rollback procedure300- Test rollback procedures in staging before production deployment301- Set a point-of-no-return threshold — define when rollback is no longer safe302- Maintain data backups taken before migration for at least one release cycle303- Rollback must not cause data loss for writes that occurred during migration304305---306307## 11. Large-Scale Migration Checklist308309### Pre-Migration310311- [ ] Document current schema and data volumes312- [ ] Identify all applications and services that access the affected tables313- [ ] Estimate migration duration based on data volume and write rate314- [ ] Create and test rollback procedures315- [ ] Set up monitoring for migration progress, error rates, and performance316- [ ] Take a consistent backup of the source data317- [ ] Communicate migration schedule to stakeholders318319### During Migration320321- [ ] Monitor replication lag and system resource usage322- [ ] Watch for lock contention and long-running queries323- [ ] Track migration progress (rows processed, estimated time remaining)324- [ ] Verify application error rates remain within acceptable bounds325- [ ] Keep the rollback plan ready for immediate execution326327### Post-Migration328329- [ ] Run data validation and reconciliation checks330- [ ] Verify application functionality with smoke tests331- [ ] Monitor performance metrics for regressions332- [ ] Update documentation and runbooks333- [ ] Schedule cleanup of deprecated schemas (contract phase)334- [ ] Conduct a post-mortem review335336## Additional References337338- For zero-downtime pattern details, see [references/zero-downtime-patterns.md](references/zero-downtime-patterns.md)339- For online schema tool comparison, see [references/online-schema-tools.md](references/online-schema-tools.md)