| name | database-migrations |
| description | Manage database schema migrations safely — Prisma Migrate, Flyway, zero-downtime migration strategies, expand-contract pattern, rollback procedures, migration testing, and production deploy sequence. Use when asked about "database migration", "Prisma migrate", "Flyway", "schema change", "add column", "rename column", "backfill data", "zero-downtime migration", "expand-contract", "migration rollback", "migrate in production", "NOT NULL column", "dropping a column safely", or "migration testing". Do NOT use for: query optimization — see database-patterns. Do NOT use for: seed data for dev — separate from migration concern.
|
| origin | yamtam-original |
| license | MIT © 2026 Vũ Văn Tâm |
| version | 1.0.0 |
| compatibility | Prisma ≥ 5.x, Flyway ≥ 10. PostgreSQL focus, MySQL patterns noted. |
When to Use
- Use when: adding/renaming/dropping columns in a live production database
- Use when: migration needs to run without taking the app offline
- Use when: a new
NOT NULL column is needed on a table with existing rows
- Use when: auditing migration history and checking for drift
- Do NOT use for: ORM query patterns — see database-patterns
- Do NOT use for: seeding dev database — use separate seed scripts
The Expand-Contract Pattern (Zero-Downtime)
Phase 1 — Expand (backward-compatible migration)
→ Add new column as nullable
→ Deploy app code that writes to BOTH old and new columns
→ Backfill existing rows
Phase 2 — Migrate (after all pods running new code)
→ Add NOT NULL constraint + set DEFAULT
→ Drop writes to old column
Phase 3 — Contract (after all pods no longer read old column)
→ Drop old column
Prisma — Safe NOT NULL Column
// ❌ Naive — breaks if table has existing rows (migration fails)
model User {
id String @id
createdAt DateTime @default(now())
// Adding directly:
timezone String // NOT NULL with no default = migration error on non-empty table
}
// ✅ Step 1: add nullable first
model User {
timezone String? // nullable — migration runs safely on any table size
}
npx prisma migrate dev --name add_user_timezone
UPDATE users SET timezone = 'UTC' WHERE timezone IS NULL;
npx prisma migrate dev --name make_timezone_not_null
Prisma Migration Commands
npx prisma migrate dev
npx prisma migrate dev --name <name>
npx prisma migrate reset
npx prisma migrate deploy
npx prisma migrate status
npx prisma db push
Flyway (Java/Spring/Multi-language)
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE users ADD COLUMN timezone VARCHAR(50);
UPDATE users SET timezone = 'UTC' WHERE timezone IS NULL;
ALTER TABLE users ALTER COLUMN timezone SET NOT NULL,
ALTER COLUMN timezone SET DEFAULT 'UTC';
flyway -url=jdbc:postgresql://localhost/mydb -user=app -password=$DB_PASS migrate
flyway validate
flyway info
Renaming a Column Safely
ALTER TABLE orders RENAME COLUMN user_id TO customer_id;
ALTER TABLE orders ADD COLUMN customer_id UUID;
UPDATE orders SET customer_id = user_id;
ALTER TABLE orders ALTER COLUMN customer_id SET NOT NULL;
ALTER TABLE orders DROP COLUMN user_id;
Dropping a Column Safely
ALTER TABLE users DROP COLUMN legacy_flag;
// Prisma: tell Prisma to ignore a column before dropping
model User {
legacyFlag Boolean? @ignore // Prisma ignores, column still exists in DB
}
// After full deploy + bake time → run DROP COLUMN migration
Migration Testing
pg_dump $PROD_DB --no-owner --no-acl > prod_snapshot.sql
psql $TEST_DB < prod_snapshot.sql
npx prisma migrate deploy
Anti-Fake-Pass Rules
Before claiming a migration is safe to run in production, you MUST show:
Reference: gates/anti-fake-pass-gate.md