Purpose & When-To-Use
Trigger conditions:
- Evolving database schema for application updates
- Performing data migrations with business logic transformations
- Implementing zero-downtime deployments requiring schema changes
- Standardizing migration workflows across teams
- Migrating between database versions or platforms
- Adding indexes, constraints, or partitioning to existing tables
Not for:
- Initial database schema creation (use ORM models or DDL scripts)
- One-off data fixes (use direct SQL with transaction safety)
- Database backups or recovery operations
- Cross-database data replication (use ETL tools)
Pre-Checks
Time normalization:
- Compute
NOW_ET using NIST/time.gov semantics (America/New_York, ISO-8601)
- Use
NOW_ET for all citation access dates
Input validation:
migration_tool must be one of: liquibase, flyway, alembic
database must be one of: postgresql, mysql, sqlserver, oracle
migration_type must be one of: schema, data, hybrid
downtime_allowed must be boolean (true/false)
migration_description must be non-empty and descriptive
Source freshness:
Procedure
T1: Basic Schema Migration (≤2k tokens)
Fast path for simple DDL changes:
Schema Change Identification
- Add/drop columns (with default values to avoid table rewrites)
- Create/drop tables
- Add/drop simple indexes (non-unique, single column)
- Add/drop NOT NULL constraints (with validation)
Tool-Specific Migration Format
Liquibase (XML/YAML) accessed 2025-10-26T03:51:54-04:00
<changeSet id="add-email-column" author="migration-generator">
<addColumn tableName="users">
<column name="email" type="VARCHAR(255)"/>
</addColumn>
<rollback>
<dropColumn tableName="users" columnName="email"/>
</rollback>
</changeSet>
Flyway (SQL) accessed 2025-10-26T03:51:54-04:00
-- V1__add_email_column.sql
ALTER TABLE users ADD COLUMN email VARCHAR(255);
Alembic (Python) accessed 2025-10-26T03:51:54-04:00
def upgrade():
op.add_column('users', sa.Column('email', sa.String(255)))
def downgrade():
op.drop_column('users', 'email')
Basic Rollback Script
- Generate inverse operation for each forward change
- Add rollback validation comments
- Include manual rollback steps if auto-rollback unsafe
Decision: If simple schema change without data → STOP at T1; otherwise proceed to T2.
T2: Data Migrations with Rollback (≤6k tokens)
Extended migrations with data transformations:
Data Migration Patterns accessed 2025-10-26T03:51:54-04:00
Backfill Existing Data
# Alembic: Backfill default values for new column
def upgrade():
op.add_column('users', sa.Column('status', sa.String(20), nullable=True))
# Backfill existing rows
op.execute("UPDATE users SET status = 'active' WHERE status IS NULL")
# Make NOT NULL after backfill
op.alter_column('users', 'status', nullable=False)
Data Transformation
# Alembic: Split full_name into first_name and last_name
def upgrade():
op.add_column('users', sa.Column('first_name', sa.String(100)))
op.add_column('users', sa.Column('last_name', sa.String(100)))
# Transform data
connection = op.get_bind()
users = connection.execute("SELECT id, full_name FROM users").fetchall()
for user_id, full_name in users:
parts = full_name.split(' ', 1)
first = parts[0]
last = parts[1] if len(parts) > 1 else ''
connection.execute(
"UPDATE users SET first_name = %s, last_name = %s WHERE id = %s",
(first, last, user_id)
)
op.drop_column('users', 'full_name')
Rollback Safety Patterns
- Idempotency: Ensure migrations can run multiple times safely
- Checkpointing: Add validation queries before destructive operations
- Backup Triggers: Create temporary backup tables for data migrations
- Dry-Run Mode: Include commented-out SELECT statements to preview changes
Validation Tests accessed 2025-10-26T03:51:54-04:00
-- Post-migration validation
SELECT COUNT(*) FROM users WHERE email IS NULL; -- Should be 0
SELECT COUNT(*) FROM users WHERE status NOT IN ('active', 'inactive'); -- Should be 0
Database-Specific Considerations
PostgreSQL accessed 2025-10-26T03:51:54-04:00
- Use
ALTER TABLE ... SET NOT NULL with CHECK constraint first
- Prefer
CONCURRENTLY for index creation (zero-downtime)
- Use
pg_stat_progress_create_index to monitor long operations
MySQL accessed 2025-10-26T03:51:54-04:00
- Check online DDL support:
ALGORITHM=INPLACE, LOCK=NONE
- Avoid
ALTER TABLE that requires table copy (pre-8.0)
- Use
pt-online-schema-change for large tables (Percona Toolkit)
SQL Server
- Use
WITH (ONLINE = ON) for index operations
- Leverage
SSMS execution plan analysis
- Consider
SCHEMA_ONLY copies for large data migrations
T3: Zero-Downtime Patterns (≤12k tokens)
Advanced patterns for production systems:
Expand/Contract Pattern accessed 2025-10-26T03:51:54-04:00
Phase 1: Expand (Add new schema)
# Migration 001: Add new column, keep old column
def upgrade():
op.add_column('users', sa.Column('email_new', sa.String(255)))
# Trigger to sync old → new during transition
op.execute("""
CREATE TRIGGER sync_email_new
BEFORE UPDATE ON users
FOR EACH ROW
BEGIN
SET NEW.email_new = NEW.email;
END;
""")
Phase 2: Migrate Data
# Migration 002: Backfill new column
def upgrade():
op.execute("UPDATE users SET email_new = email WHERE email_new IS NULL")
Phase 3: Contract (Remove old schema)
# Migration 003: Drop old column (after application updated)
def upgrade():
op.execute("DROP TRIGGER IF EXISTS sync_email_new")
op.drop_column('users', 'email')
op.alter_column('users', 'email_new', new_column_name='email')
Online Index Creation accessed 2025-10-26T03:51:54-04:00
PostgreSQL CONCURRENTLY
-- Flyway: V5__add_email_index.sql
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
-- Validation
SELECT schemaname, tablename, indexname, indexdef
FROM pg_indexes
WHERE indexname = 'idx_users_email';
MySQL Online DDL
-- Flyway: V6__add_composite_index.sql
ALTER TABLE users
ADD INDEX idx_email_status (email, status)
ALGORITHM=INPLACE, LOCK=NONE;
Shadow Table Pattern (for complex transformations)
# Migration 010: Create shadow table with new schema
def upgrade():
op.create_table(
'users_new',
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('email', sa.String(255), nullable=False, index=True),
sa.Column('status', sa.String(20), nullable=False)
)
# Stream data from old → new table
op.execute("""
INSERT INTO users_new (id, email, status)
SELECT id, email, COALESCE(status, 'active')
FROM users
""")
# Atomic rename (downtime: milliseconds)
op.rename_table('users', 'users_old')
op.rename_table('users_new', 'users')
Deployment Guide Template
## Deployment Steps
### Pre-Migration
1. Verify database backup completed (last 24h)
2. Check application connection pool settings (timeout ≥ 30s)
3. Review query performance baseline (pg_stat_statements)
### Migration Execution
1. Run migration in transaction (if supported)
2. Monitor lock waits: `SELECT * FROM pg_locks WHERE NOT granted`
3. Validate row counts: `SELECT COUNT(*) FROM users`
### Post-Migration
1. Run ANALYZE to update statistics
2. Verify application logs (no constraint violations)
3. Monitor query performance (compare to baseline)
### Rollback Procedure (if needed)
1. Stop application traffic (or use feature flag)
2. Run rollback script: `flyway undo` or `alembic downgrade -1`
3. Verify data integrity: `SELECT * FROM users LIMIT 10`
4. Restore from backup if rollback fails
Blue-Green Database Migrations accessed 2025-10-26T03:51:54-04:00
- Duplicate database instance (Blue = old schema, Green = new schema)
- Run migrations on Green instance
- Dual-write pattern during transition (application writes to both)
- Cutover: Update connection string to Green
- Validation period: Keep Blue online for 24-48h
Decision Rules
Migration Tool Selection:
- Liquibase: Best for multi-database support, XML/YAML declarative changes, complex rollback
- Flyway: Best for SQL-first teams, simple versioning, Java/Spring ecosystems
- Alembic: Best for Python applications using SQLAlchemy, programmatic migrations
Migration Strategy by Downtime Allowance:
- downtime_allowed = true: Use direct ALTER TABLE, faster execution, simpler scripts
- downtime_allowed = false: Use expand/contract, CONCURRENTLY, shadow tables, longer timeline
Database-Specific Patterns:
- PostgreSQL: Prefer CONCURRENTLY for indexes, use CHECK constraints before NOT NULL
- MySQL: Validate ALGORITHM=INPLACE support, use pt-online-schema-change for InnoDB
- SQL Server: Use consider columnstore indexes for analytics workloads
- Oracle: Use Oracle Data Redefinition (DBMS_REDEFINITION) for zero-downtime
Abort Conditions:
- Invalid tool/database combination → error "Tool X does not support database Y"
- Destructive operation without rollback → error "Cannot generate safe rollback for DROP TABLE"
- Zero-downtime requested for non-supported operation → error "Zero-downtime not possible for operation X"
Data Preservation Checks:
- Dropping columns: Warn if column contains non-NULL data
- Changing types: Validate data fits in new type (VARCHAR(50) → VARCHAR(20))
- Adding NOT NULL: Require default value or backfill strategy
Output Contract
Schema (JSON):
{
"migration_tool": "liquibase | flyway | alembic",
"database": "postgresql | mysql | sqlserver | oracle",
"migration_type": "schema | data | hybrid",
"downtime_allowed": "boolean",
"migration_script": {
"filename": "string (e.g., V5__add_email_column.sql)",
"content": "string (tool-specific migration code)"
},
"rollback_script": {
"filename": "string (e.g., U5__undo_email_column.sql)",
"content": "string (inverse migration code)",
"manual_steps": ["string (if auto-rollback unsafe)"]
},
"validation_tests": [
{
"description": "string",
"query": "string (SQL validation query)",
"expected_result": "string"
}
],
"deployment_guide": {
"pre_migration_steps": ["string"],
"execution_steps": ["string"],
"post_migration_steps": ["string"],
"rollback_procedure": ["string"],
"estimated_duration": "string (e.g., '5 minutes', '2 hours')"
},
"warnings": ["string (potential issues or breaking changes)"],
"timestamp": "ISO-8601 string (NOW_ET)"
}
Required Fields:
migration_tool, database, migration_type, downtime_allowed, migration_script, rollback_script, validation_tests, deployment_guide, timestamp
Safety Guarantees:
- All DDL changes must have explicit rollback (or manual rollback steps)
- Data migrations must include row count validation
- Zero-downtime migrations must specify lock duration estimates
Examples
Example 1: Simple Column Addition (Alembic + PostgreSQL)
"""Add email column to users table with NOT NULL constraint
Revision ID: a1b2c3d4e5f6
Revises: previous_revision
Create Date: 2025-10-26 03:51:54.000000
"""
from alembic import op
import sqlalchemy as sa
def upgrade():
# Add column as nullable first
op.add_column('users', sa.Column('email', sa.String(255), nullable=True))
# Backfill with placeholder (application will update)
op.execute("UPDATE users SET email = CONCAT('user', id, '@example.com') WHERE email IS NULL")
# Add NOT NULL constraint
op.alter_column('users', 'email', nullable=False)
# Add index for performance
op.create_index('idx_users_email', 'users', ['email'], unique=True)
def downgrade():
op.drop_index('idx_users_email', table_name='users')
op.drop_column('users', 'email')
Quality Gates
Token Budgets:
- T1: ≤2k tokens (simple schema change, basic rollback)
- T2: ≤6k tokens (data migration, validation tests, database-specific optimizations)
- T3: ≤12k tokens (zero-downtime patterns, deployment guide, multi-phase migrations)
Safety:
- No plaintext credentials in migration scripts (use environment variables)
- All destructive operations require explicit confirmation comments
- Rollback scripts tested against sample data
Auditability:
- Migration IDs/versions follow tool conventions (Flyway: V1__description.sql, Alembic: revision IDs)
- All migrations include author, timestamp, and description
- Database-specific syntax validated against official documentation
Determinism:
- Same inputs → identical migration scripts
- Idempotent migrations (can run multiple times safely)
- Predictable rollback behavior
Performance:
- Estimate lock duration for DDL operations
- Include EXPLAIN ANALYZE for data migrations affecting >10k rows
- Recommend batch size for large table transformations (e.g., 1000 rows/batch)
Resources
Official Documentation (accessed 2025-10-26T03:51:54-04:00):
- Liquibase Change Types - DDL/DML operations
- Flyway SQL Migrations - Versioned migrations
- Alembic Operations Reference - Python migration API
- PostgreSQL ALTER TABLE - DDL syntax
- MySQL Online DDL - Zero-downtime operations
- SQL Server Online Index Operations - Online DDL
Migration Patterns:
Best Practices:
Tool Comparisons:
1---2name: database-migration-script-generator3description: Generate database migration scripts for Liquibase, Flyway, Alembic with rollback safety, data preservation, and zero-downtime patterns4license: MIT5---67## Purpose & When-To-Use89**Trigger conditions:**10- Evolving database schema for application updates11- Performing data migrations with business logic transformations12- Implementing zero-downtime deployments requiring schema changes13- Standardizing migration workflows across teams14- Migrating between database versions or platforms15- Adding indexes, constraints, or partitioning to existing tables1617**Not for:**18- Initial database schema creation (use ORM models or DDL scripts)19- One-off data fixes (use direct SQL with transaction safety)20- Database backups or recovery operations21- Cross-database data replication (use ETL tools)2223---2425## Pre-Checks2627**Time normalization:**28- Compute `NOW_ET` using NIST/time.gov semantics (America/New_York, ISO-8601)29- Use `NOW_ET` for all citation access dates3031**Input validation:**32- `migration_tool` must be one of: liquibase, flyway, alembic33- `database` must be one of: postgresql, mysql, sqlserver, oracle34- `migration_type` must be one of: schema, data, hybrid35- `downtime_allowed` must be boolean (true/false)36- `migration_description` must be non-empty and descriptive3738**Source freshness:**39- Liquibase docs must be accessible [accessed 2025-10-26T03:51:54-04:00](https://docs.liquibase.com/change-types/home.html)40- Flyway docs must be accessible [accessed 2025-10-26T03:51:54-04:00](https://flywaydb.org/documentation/concepts/migrations)41- Alembic docs must be accessible [accessed 2025-10-26T03:51:54-04:00](https://alembic.sqlalchemy.org/en/latest/tutorial.html)42- PostgreSQL online DDL docs must be accessible [accessed 2025-10-26T03:51:54-04:00](https://www.postgresql.org/docs/current/sql-altertable.html)43- MySQL online DDL docs must be accessible [accessed 2025-10-26T03:51:54-04:00](https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl.html)4445---4647## Procedure4849### T1: Basic Schema Migration (≤2k tokens)5051**Fast path for simple DDL changes:**52531. **Schema Change Identification**54 - Add/drop columns (with default values to avoid table rewrites)55 - Create/drop tables56 - Add/drop simple indexes (non-unique, single column)57 - Add/drop NOT NULL constraints (with validation)58592. **Tool-Specific Migration Format**6061 **Liquibase (XML/YAML)** [accessed 2025-10-26T03:51:54-04:00](https://docs.liquibase.com/change-types/add-column.html)62 ```xml63 <changeSet id="add-email-column" author="migration-generator">64 <addColumn tableName="users">65 <column name="email" type="VARCHAR(255)"/>66 </addColumn>67 <rollback>68 <dropColumn tableName="users" columnName="email"/>69 </rollback>70 </changeSet>71 ```7273 **Flyway (SQL)** [accessed 2025-10-26T03:51:54-04:00](https://flywaydb.org/documentation/concepts/migrations#sql-based-migrations)74 ```sql75 -- V1__add_email_column.sql76 ALTER TABLE users ADD COLUMN email VARCHAR(255);77 ```7879 **Alembic (Python)** [accessed 2025-10-26T03:51:54-04:00](https://alembic.sqlalchemy.org/en/latest/ops.html#alembic.operations.Operations.add_column)80 ```python81 def upgrade():82 op.add_column('users', sa.Column('email', sa.String(255)))8384 def downgrade():85 op.drop_column('users', 'email')86 ```87883. **Basic Rollback Script**89 - Generate inverse operation for each forward change90 - Add rollback validation comments91 - Include manual rollback steps if auto-rollback unsafe9293**Decision:** If simple schema change without data → STOP at T1; otherwise proceed to T2.9495---9697### T2: Data Migrations with Rollback (≤6k tokens)9899**Extended migrations with data transformations:**1001011. **Data Migration Patterns** [accessed 2025-10-26T03:51:54-04:00](https://docs.liquibase.com/change-types/update.html)102103 **Backfill Existing Data**104 ```python105 # Alembic: Backfill default values for new column106 def upgrade():107 op.add_column('users', sa.Column('status', sa.String(20), nullable=True))108 # Backfill existing rows109 op.execute("UPDATE users SET status = 'active' WHERE status IS NULL")110 # Make NOT NULL after backfill111 op.alter_column('users', 'status', nullable=False)112 ```113114 **Data Transformation**115 ```python116 # Alembic: Split full_name into first_name and last_name117 def upgrade():118 op.add_column('users', sa.Column('first_name', sa.String(100)))119 op.add_column('users', sa.Column('last_name', sa.String(100)))120 # Transform data121 connection = op.get_bind()122 users = connection.execute("SELECT id, full_name FROM users").fetchall()123 for user_id, full_name in users:124 parts = full_name.split(' ', 1)125 first = parts[0]126 last = parts[1] if len(parts) > 1 else ''127 connection.execute(128 "UPDATE users SET first_name = %s, last_name = %s WHERE id = %s",129 (first, last, user_id)130 )131 op.drop_column('users', 'full_name')132 ```1331342. **Rollback Safety Patterns**135 - **Idempotency:** Ensure migrations can run multiple times safely136 - **Checkpointing:** Add validation queries before destructive operations137 - **Backup Triggers:** Create temporary backup tables for data migrations138 - **Dry-Run Mode:** Include commented-out SELECT statements to preview changes1391403. **Validation Tests** [accessed 2025-10-26T03:51:54-04:00](https://flywaydb.org/documentation/concepts/callbacks)141 ```sql142 -- Post-migration validation143 SELECT COUNT(*) FROM users WHERE email IS NULL; -- Should be 0144 SELECT COUNT(*) FROM users WHERE status NOT IN ('active', 'inactive'); -- Should be 0145 ```1461474. **Database-Specific Considerations**148149 **PostgreSQL** [accessed 2025-10-26T03:51:54-04:00](https://www.postgresql.org/docs/current/ddl-alter.html)150 - Use `ALTER TABLE ... SET NOT NULL` with `CHECK` constraint first151 - Prefer `CONCURRENTLY` for index creation (zero-downtime)152 - Use `pg_stat_progress_create_index` to monitor long operations153154 **MySQL** [accessed 2025-10-26T03:51:54-04:00](https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html)155 - Check online DDL support: `ALGORITHM=INPLACE, LOCK=NONE`156 - Avoid `ALTER TABLE` that requires table copy (pre-8.0)157 - Use `pt-online-schema-change` for large tables (Percona Toolkit)158159 **SQL Server**160 - Use `WITH (ONLINE = ON)` for index operations161 - Leverage `SSMS` execution plan analysis162 - Consider `SCHEMA_ONLY` copies for large data migrations163164---165166### T3: Zero-Downtime Patterns (≤12k tokens)167168**Advanced patterns for production systems:**1691701. **Expand/Contract Pattern** [accessed 2025-10-26T03:51:54-04:00](https://www.liquibase.com/blog/expand-contract-pattern)171172 **Phase 1: Expand (Add new schema)**173 ```python174 # Migration 001: Add new column, keep old column175 def upgrade():176 op.add_column('users', sa.Column('email_new', sa.String(255)))177 # Trigger to sync old → new during transition178 op.execute("""179 CREATE TRIGGER sync_email_new180 BEFORE UPDATE ON users181 FOR EACH ROW182 BEGIN183 SET NEW.email_new = NEW.email;184 END;185 """)186 ```187188 **Phase 2: Migrate Data**189 ```python190 # Migration 002: Backfill new column191 def upgrade():192 op.execute("UPDATE users SET email_new = email WHERE email_new IS NULL")193 ```194195 **Phase 3: Contract (Remove old schema)**196 ```python197 # Migration 003: Drop old column (after application updated)198 def upgrade():199 op.execute("DROP TRIGGER IF EXISTS sync_email_new")200 op.drop_column('users', 'email')201 op.alter_column('users', 'email_new', new_column_name='email')202 ```2032042. **Online Index Creation** [accessed 2025-10-26T03:51:54-04:00](https://www.postgresql.org/docs/current/sql-createindex.html#SQL-CREATEINDEX-CONCURRENTLY)205206 **PostgreSQL CONCURRENTLY**207 ```sql208 -- Flyway: V5__add_email_index.sql209 CREATE INDEX CONCURRENTLY idx_users_email ON users(email);210211 -- Validation212 SELECT schemaname, tablename, indexname, indexdef213 FROM pg_indexes214 WHERE indexname = 'idx_users_email';215 ```216217 **MySQL Online DDL**218 ```sql219 -- Flyway: V6__add_composite_index.sql220 ALTER TABLE users221 ADD INDEX idx_email_status (email, status)222 ALGORITHM=INPLACE, LOCK=NONE;223 ```2242253. **Shadow Table Pattern** (for complex transformations)226 ```python227 # Migration 010: Create shadow table with new schema228 def upgrade():229 op.create_table(230 'users_new',231 sa.Column('id', sa.Integer, primary_key=True),232 sa.Column('email', sa.String(255), nullable=False, index=True),233 sa.Column('status', sa.String(20), nullable=False)234 )235 # Stream data from old → new table236 op.execute("""237 INSERT INTO users_new (id, email, status)238 SELECT id, email, COALESCE(status, 'active')239 FROM users240 """)241 # Atomic rename (downtime: milliseconds)242 op.rename_table('users', 'users_old')243 op.rename_table('users_new', 'users')244 ```2452464. **Deployment Guide Template**247 ```markdown248 ## Deployment Steps249250 ### Pre-Migration251 1. Verify database backup completed (last 24h)252 2. Check application connection pool settings (timeout ≥ 30s)253 3. Review query performance baseline (pg_stat_statements)254255 ### Migration Execution256 1. Run migration in transaction (if supported)257 2. Monitor lock waits: `SELECT * FROM pg_locks WHERE NOT granted`258 3. Validate row counts: `SELECT COUNT(*) FROM users`259260 ### Post-Migration261 1. Run ANALYZE to update statistics262 2. Verify application logs (no constraint violations)263 3. Monitor query performance (compare to baseline)264265 ### Rollback Procedure (if needed)266 1. Stop application traffic (or use feature flag)267 2. Run rollback script: `flyway undo` or `alembic downgrade -1`268 3. Verify data integrity: `SELECT * FROM users LIMIT 10`269 4. Restore from backup if rollback fails270 ```2712725. **Blue-Green Database Migrations** [accessed 2025-10-26T03:51:54-04:00](https://martinfowler.com/bliki/BlueGreenDeployment.html)273 - Duplicate database instance (Blue = old schema, Green = new schema)274 - Run migrations on Green instance275 - Dual-write pattern during transition (application writes to both)276 - Cutover: Update connection string to Green277 - Validation period: Keep Blue online for 24-48h278279---280281## Decision Rules282283**Migration Tool Selection:**284- **Liquibase:** Best for multi-database support, XML/YAML declarative changes, complex rollback285- **Flyway:** Best for SQL-first teams, simple versioning, Java/Spring ecosystems286- **Alembic:** Best for Python applications using SQLAlchemy, programmatic migrations287288**Migration Strategy by Downtime Allowance:**289- **downtime_allowed = true:** Use direct ALTER TABLE, faster execution, simpler scripts290- **downtime_allowed = false:** Use expand/contract, CONCURRENTLY, shadow tables, longer timeline291292**Database-Specific Patterns:**293- **PostgreSQL:** Prefer CONCURRENTLY for indexes, use CHECK constraints before NOT NULL294- **MySQL:** Validate ALGORITHM=INPLACE support, use pt-online-schema-change for InnoDB295- **SQL Server:** Use ONLINE=ON, consider columnstore indexes for analytics workloads296- **Oracle:** Use Oracle Data Redefinition (DBMS_REDEFINITION) for zero-downtime297298**Abort Conditions:**299- Invalid tool/database combination → error "Tool X does not support database Y"300- Destructive operation without rollback → error "Cannot generate safe rollback for DROP TABLE"301- Zero-downtime requested for non-supported operation → error "Zero-downtime not possible for operation X"302303**Data Preservation Checks:**304- Dropping columns: Warn if column contains non-NULL data305- Changing types: Validate data fits in new type (VARCHAR(50) → VARCHAR(20))306- Adding NOT NULL: Require default value or backfill strategy307308---309310## Output Contract311312**Schema (JSON):**313314```json315{316 "migration_tool": "liquibase | flyway | alembic",317 "database": "postgresql | mysql | sqlserver | oracle",318 "migration_type": "schema | data | hybrid",319 "downtime_allowed": "boolean",320 "migration_script": {321 "filename": "string (e.g., V5__add_email_column.sql)",322 "content": "string (tool-specific migration code)"323 },324 "rollback_script": {325 "filename": "string (e.g., U5__undo_email_column.sql)",326 "content": "string (inverse migration code)",327 "manual_steps": ["string (if auto-rollback unsafe)"]328 },329 "validation_tests": [330 {331 "description": "string",332 "query": "string (SQL validation query)",333 "expected_result": "string"334 }335 ],336 "deployment_guide": {337 "pre_migration_steps": ["string"],338 "execution_steps": ["string"],339 "post_migration_steps": ["string"],340 "rollback_procedure": ["string"],341 "estimated_duration": "string (e.g., '5 minutes', '2 hours')"342 },343 "warnings": ["string (potential issues or breaking changes)"],344 "timestamp": "ISO-8601 string (NOW_ET)"345}346```347348**Required Fields:**349- `migration_tool`, `database`, `migration_type`, `downtime_allowed`, `migration_script`, `rollback_script`, `validation_tests`, `deployment_guide`, `timestamp`350351**Safety Guarantees:**352- All DDL changes must have explicit rollback (or manual rollback steps)353- Data migrations must include row count validation354- Zero-downtime migrations must specify lock duration estimates355356---357358## Examples359360**Example 1: Simple Column Addition (Alembic + PostgreSQL)**361362```python363"""Add email column to users table with NOT NULL constraint364365Revision ID: a1b2c3d4e5f6366Revises: previous_revision367Create Date: 2025-10-26 03:51:54.000000368369"""370from alembic import op371import sqlalchemy as sa372373def upgrade():374 # Add column as nullable first375 op.add_column('users', sa.Column('email', sa.String(255), nullable=True))376377 # Backfill with placeholder (application will update)378 op.execute("UPDATE users SET email = CONCAT('user', id, '@example.com') WHERE email IS NULL")379380 # Add NOT NULL constraint381 op.alter_column('users', 'email', nullable=False)382383 # Add index for performance384 op.create_index('idx_users_email', 'users', ['email'], unique=True)385386def downgrade():387 op.drop_index('idx_users_email', table_name='users')388 op.drop_column('users', 'email')389```390391---392393## Quality Gates394395**Token Budgets:**396- **T1:** ≤2k tokens (simple schema change, basic rollback)397- **T2:** ≤6k tokens (data migration, validation tests, database-specific optimizations)398- **T3:** ≤12k tokens (zero-downtime patterns, deployment guide, multi-phase migrations)399400**Safety:**401- No plaintext credentials in migration scripts (use environment variables)402- All destructive operations require explicit confirmation comments403- Rollback scripts tested against sample data404405**Auditability:**406- Migration IDs/versions follow tool conventions (Flyway: V1__description.sql, Alembic: revision IDs)407- All migrations include author, timestamp, and description408- Database-specific syntax validated against official documentation409410**Determinism:**411- Same inputs → identical migration scripts412- Idempotent migrations (can run multiple times safely)413- Predictable rollback behavior414415**Performance:**416- Estimate lock duration for DDL operations417- Include EXPLAIN ANALYZE for data migrations affecting >10k rows418- Recommend batch size for large table transformations (e.g., 1000 rows/batch)419420---421422## Resources423424**Official Documentation (accessed 2025-10-26T03:51:54-04:00):**4251. [Liquibase Change Types](https://docs.liquibase.com/change-types/home.html) - DDL/DML operations4262. [Flyway SQL Migrations](https://flywaydb.org/documentation/concepts/migrations) - Versioned migrations4273. [Alembic Operations Reference](https://alembic.sqlalchemy.org/en/latest/ops.html) - Python migration API4284. [PostgreSQL ALTER TABLE](https://www.postgresql.org/docs/current/sql-altertable.html) - DDL syntax4295. [MySQL Online DDL](https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl.html) - Zero-downtime operations4306. [SQL Server Online Index Operations](https://learn.microsoft.com/en-us/sql/relational-databases/indexes/perform-index-operations-online) - Online DDL431432**Migration Patterns:**433- [Expand/Contract Pattern](https://www.liquibase.com/blog/expand-contract-pattern) - Zero-downtime schema evolution434- [Blue-Green Deployments](https://martinfowler.com/bliki/BlueGreenDeployment.html) - Database migration strategies435- [Database Refactoring](https://databaserefactoring.com/) - Catalog of database refactoring patterns436437**Best Practices:**438- [Flyway Best Practices](https://flywaydb.org/documentation/usage/bestpractices) - Migration versioning and naming439- [Alembic Tutorial](https://alembic.sqlalchemy.org/en/latest/tutorial.html) - Auto-generate vs manual migrations440- [PostgreSQL Wiki: Don't Do This](https://wiki.postgresql.org/wiki/Don%27t_Do_This) - Anti-patterns to avoid441442**Tool Comparisons:**443- [Liquibase vs Flyway](https://www.liquibase.com/liquibase-vs-flyway) - Feature comparison444- [Schema Migration Tools Comparison](https://db-migrations.github.io/) - Multi-tool benchmarks