Database Migration Workflow
Safe schema changes and data migration workflow with rollback strategies and zero-downtime deployment
The Database Migration workflow provides a structured, safe approach to schema changes and data migrations, ensuring data integrity, minimal downtime, and the ability to rollback if issues arise.
Overview#
| Property | Value |
|---|---|
| Phases | 4 |
| Tier | Free |
| Typical Duration | 5-6 days |
| Best For | Schema changes, data transformations, database refactoring |
Outcomes#
A successful database migration results in:
- Migration completed safely with rollback readiness
- Migration rehearsed in staging environment
- Rollback procedure tested and documented
- Data integrity checks passing
Phases#
Phase 1: Schema Design (1 day)#
Agents: database-expert
Design the new schema changes with careful consideration for backward compatibility and data integrity.
Tasks:
- Document current schema state
- Design target schema changes
- Identify breaking vs non-breaking changes
- Plan for backward compatibility
- Define data transformation requirements
- Create migration scripts
Migration Types:
┌────────────────────────────────────────────────────────────┐
│ Migration Types │
├─────────────────┬──────────────────────────────────────────┤
│ Non-Breaking │ Add column (nullable), add table, │
│ │ add index, expand column type │
├─────────────────┼──────────────────────────────────────────┤
│ Breaking │ Remove column, rename column, │
│ │ change column type, drop table │
├─────────────────┼──────────────────────────────────────────┤
│ Data Migration │ Transform existing data, backfill │
│ │ values, merge/split tables │
└─────────────────┴──────────────────────────────────────────┘
Schema Change Example:
1// Before
2model User {
3 id String @id @default(cuid())
4 name String
5 email String @unique
6 createdAt DateTime @default(now())
7}
8
9// After - Adding new fields (non-breaking)
10model User {
11 id String @id @default(cuid())
12 name String
13 email String @unique
14 avatarUrl String? // New nullable field
15 lastLoginAt DateTime? // New nullable field
16 createdAt DateTime @default(now())
17 updatedAt DateTime @updatedAt // New field with default
18}Phase 2: Migration Plan (1 day)#
Agents: database-expert, devops-expert
Create a detailed migration plan including timing, rollback procedures, and communication strategy.
Tasks:
- Define migration window and timing
- Create step-by-step execution plan
- Document rollback procedures
- Plan data validation checks
- Coordinate with stakeholders
- Set up monitoring for migration
Migration Plan Template:
1## Migration: [Name]
2**Date:** YYYY-MM-DD
3**Window:** HH:MM - HH:MM (timezone)
4**Risk Level:** Low/Medium/High
5
6### Pre-Migration Checklist
7- [ ] Backup completed and verified
8- [ ] Staging migration successful
9- [ ] Rollback tested
10- [ ] Team notified
11- [ ] Monitoring dashboards open
12
13### Execution Steps
141. Enable maintenance mode (if needed)
152. Take final backup
163. Run migration: `npx prisma migrate deploy`
174. Verify data integrity
185. Run application smoke tests
196. Disable maintenance mode
20
21### Rollback Procedure
221. Enable maintenance mode
232. Restore from backup
243. Revert application code
254. Verify rollback success
265. Disable maintenance mode
27
28### Success Criteria
29- [ ] All migrations applied
30- [ ] Data integrity checks pass
31- [ ] Application functional
32- [ ] No error spikes in monitoringPhase 3: Implementation (2-3 days)#
Agents: database-expert, backend-expert
Execute the migration with proper staging validation and production deployment.
Tasks:
- Test migration in development
- Run migration in staging environment
- Validate staging data integrity
- Execute production migration
- Run data integrity checks
- Update application code as needed
Prisma Migration Commands:
1# Create migration (development)
2npx prisma migrate dev --name add_user_avatar
3
4# Check migration status
5npx prisma migrate status
6
7# Deploy to production
8npx prisma migrate deploy
9
10# Reset database (development only!)
11npx prisma migrate resetZero-Downtime Pattern:
Phase 1: Deploy code that handles both schemas
(reads new column, falls back to old)
│
▼
Phase 2: Run migration to add new column
│
▼
Phase 3: Backfill data for new column
│
▼
Phase 4: Deploy code that requires new column
│
▼
Phase 5: Run migration to remove old column
Backfill Script Example:
1// scripts/backfill-avatar-urls.ts
2import { prisma } from '@/lib/prisma';
3
4async function backfillAvatarUrls() {
5 const batchSize = 1000;
6 let processed = 0;
7 let hasMore = true;
8
9 while (hasMore) {
10 const users = await prisma.user.findMany({
11 where: { avatarUrl: null },
12 take: batchSize,
13 select: { id: true, email: true }
14 });
15
16 if (users.length === 0) {
17 hasMore = false;
18 continue;
19 }
20
21 await prisma.$transaction(
22 users.map(user =>
23 prisma.user.update({
24 where: { id: user.id },
25 data: {
26 avatarUrl: generateGravatarUrl(user.email)
27 }
28 })
29 )
30 );
31
32 processed += users.length;
33 console.log(`Processed ${processed} users`);
34 }
35
36 console.log('Backfill complete');
37}
38
39backfillAvatarUrls();Phase 4: Verification (1 day)#
Agents: testing-expert
Verify the migration was successful and all data integrity requirements are met.
Tasks:
- Run data integrity checks
- Verify row counts match expectations
- Test application functionality
- Validate API responses
- Check for data inconsistencies
- Document migration results
Data Integrity Checks:
1-- Verify row counts
2SELECT 'users' as table_name, COUNT(*) as count FROM users
3UNION ALL
4SELECT 'orders', COUNT(*) FROM orders;
5
6-- Check for orphaned records
7SELECT o.id FROM orders o
8LEFT JOIN users u ON o.user_id = u.id
9WHERE u.id IS NULL;
10
11-- Verify not null constraints
12SELECT COUNT(*) as null_count
13FROM users
14WHERE avatar_url IS NULL AND created_at < '2024-01-01';
15
16-- Check data consistency
17SELECT id, email FROM users
18WHERE email NOT LIKE '%@%.%';Starting the Workflow#
1# Start the workflow
2bootspring workflow start database-migration
3
4# Check current status
5bootspring workflow status
6
7# Advance to next phase
8bootspring workflow next
9
10# Mark a checkpoint complete
11bootspring workflow checkpoint "Migration rehearsed"Completion Signals#
Track progress with these checkpoints:
- Migration rehearsed - Successfully tested in staging
- Rollback tested - Rollback procedure verified
- Data integrity checks passing - All validation queries pass
Best Practices#
Always Have a Backup#
# PostgreSQL backup
pg_dump -Fc database_name > backup_$(date +%Y%m%d_%H%M%S).dump
# Restore from backup
pg_restore -d database_name backup.dumpUse Transactions#
1// Wrap related changes in a transaction
2await prisma.$transaction(async (tx) => {
3 // Create new record
4 const newRecord = await tx.newTable.create({
5 data: transformedData
6 });
7
8 // Update reference
9 await tx.oldTable.update({
10 where: { id: oldRecord.id },
11 data: { newTableId: newRecord.id }
12 });
13});Batch Large Updates#
1// Process large datasets in batches
2const BATCH_SIZE = 1000;
3let skip = 0;
4let hasMore = true;
5
6while (hasMore) {
7 const records = await prisma.record.findMany({
8 take: BATCH_SIZE,
9 skip,
10 });
11
12 if (records.length < BATCH_SIZE) {
13 hasMore = false;
14 }
15
16 await processBatch(records);
17 skip += BATCH_SIZE;
18}Monitor During Migration#
1// Log migration progress
2console.log(`[${new Date().toISOString()}] Starting migration`);
3console.log(`Records to process: ${totalCount}`);
4
5// Track timing
6const startTime = Date.now();
7// ... migration code ...
8const duration = Date.now() - startTime;
9console.log(`Migration completed in ${duration}ms`);Tips for Success#
- Never skip staging - Always test migrations in a production-like environment
- Plan for failure - Have a tested rollback procedure ready
- Communicate early - Notify stakeholders about planned downtime
- Batch large operations - Avoid long-running transactions
- Monitor closely - Watch for errors and performance issues during migration