3.1 KiB
3.1 KiB
name: database-migrations
description: Database migration patterns for Prisma, Drizzle, Django, SQLAlchemy, Go migrations. Use when: schema changes, adding columns/tables/indexes, data migrations, rollback strategies.
Database Migration Patterns
Based on ECC database-migrations skill.
Universal Rules
- Every migration is reversible — always write DOWN (rollback) migration
- Deploy in phases when possible:
- Phase 1: Add new column/table (non-breaking)
- Phase 2: Backfill data / dual-write
- Phase 3: Switch reads to new schema
- Phase 4: Remove old column/table
- Never drop data in a migration without explicit user confirmation
- Test migrations against a copy of production data
- Index new columns that will be queried frequently
- Add NOT NULL with DEFAULT when adding columns to existing tables
Migration Strategy by Framework
Prisma (TypeScript)
npx prisma migrate dev --name add_user_role
- Edit schema.prisma → generate migration → review → apply
- Always check the generated SQL
Drizzle (TypeScript)
npx drizzle-kit generate:pg --name add_user_role
- Migration files are TypeScript
- Easier to review and modify than raw SQL
Django (Python)
python manage.py makemigrations
python manage.py migrate
python manage.py showmigrations
- Django auto-detects changes
- For data migrations: use RunPython
- For complex operations: use migrations.RunSQL
SQLAlchemy (Python)
alembic revision --autogenerate -m "add_user_role"
alembic upgrade head
- Review auto-generated migrations carefully
- Add missing operations manually
Go (goose, golang-migrate)
goose create add_user_role sql
# or
migrate create -ext sql -dir migrations -seq add_user_role
- SQL files: up and down
- Explicit and reviewable
Data Migration Patterns
Adding a NOT NULL column with default
-- Safe: existing rows get default value
ALTER TABLE users ADD COLUMN role VARCHAR(50) NOT NULL DEFAULT 'user';
-- Then optionally remove default
ALTER TABLE users ALTER COLUMN role DROP DEFAULT;
Adding an index
-- Use CONCURRENTLY in production (PostgreSQL)
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
Renaming a column (zero-downtime)
- Add new column
- Deploy code that writes to both columns
- Backfill data from old to new
- Switch reads to new column
- Remove old column
Backfill patterns
-- Batch to avoid locking
UPDATE users SET status = 'active'
WHERE id IN (
SELECT id FROM users WHERE status = 'pending'
LIMIT 10000
);
Rollback Testing
Before deploying:
- Apply migration on staging
- Roll it back
- Verify data integrity
- Re-apply to confirm idempotent
Checklist
- Migration file created with descriptive name
- DOWN migration written and tested
- Migration tested on staging DB
- Indexes added for new query columns
- NOT NULL columns have DEFAULT values
- No raw DROP TABLE (use CASCADE with caution)
- Data migrations are idempotent
- Migration runs within acceptable time (< 5 min for online)