117 lines
3.1 KiB
Markdown
117 lines
3.1 KiB
Markdown
---
|
|
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
|
|
|
|
1. **Every migration is reversible** — always write DOWN (rollback) migration
|
|
2. **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
|
|
3. **Never drop data in a migration** without explicit user confirmation
|
|
4. **Test migrations** against a copy of production data
|
|
5. **Index new columns** that will be queried frequently
|
|
6. **Add NOT NULL with DEFAULT** when adding columns to existing tables
|
|
|
|
## Migration Strategy by Framework
|
|
|
|
### Prisma (TypeScript)
|
|
```bash
|
|
npx prisma migrate dev --name add_user_role
|
|
```
|
|
- Edit schema.prisma → generate migration → review → apply
|
|
- Always check the generated SQL
|
|
|
|
### Drizzle (TypeScript)
|
|
```bash
|
|
npx drizzle-kit generate:pg --name add_user_role
|
|
```
|
|
- Migration files are TypeScript
|
|
- Easier to review and modify than raw SQL
|
|
|
|
### Django (Python)
|
|
```bash
|
|
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)
|
|
```bash
|
|
alembic revision --autogenerate -m "add_user_role"
|
|
alembic upgrade head
|
|
```
|
|
- Review auto-generated migrations carefully
|
|
- Add missing operations manually
|
|
|
|
### Go (goose, golang-migrate)
|
|
```bash
|
|
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
|
|
```sql
|
|
-- 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
|
|
```sql
|
|
-- Use CONCURRENTLY in production (PostgreSQL)
|
|
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
|
|
```
|
|
|
|
### Renaming a column (zero-downtime)
|
|
1. Add new column
|
|
2. Deploy code that writes to both columns
|
|
3. Backfill data from old to new
|
|
4. Switch reads to new column
|
|
5. Remove old column
|
|
|
|
### Backfill patterns
|
|
```sql
|
|
-- 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:
|
|
1. Apply migration on staging
|
|
2. Roll it back
|
|
3. Verify data integrity
|
|
4. 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)
|