Source:
ocean/docs/adr/0050-database-migration-strategy.md| ✏️ Edit on GitHub
ADR-050: Database Migration Strategy and Production Deployment
Status
Proposed
Context
We are experiencing critical issues with database migrations in production:
- Schema Drift: Production database has migrations that don't exist in the repository
- CI/CD Workarounds: Current deployment creates "placeholder" migrations for remote-only migrations
- Failed User Signups: Schema mismatches cause runtime errors (e.g.,
errorvserror_messagecolumn) - Lack of Visibility: No clear way to verify migration state between environments
- Manual Interventions: Migrations applied directly to production without version control
This approach is dangerous and unsustainable as it:
- Breaks reproducibility across environments
- Makes rollbacks impossible
- Causes production incidents
- Violates infrastructure-as-code principles
Decision
We will implement a strict, future-proof migration strategy based on these principles:
1. Single Source of Truth
- All migrations MUST be in version control before being applied anywhere
- No direct SQL execution in production outside of emergencies
- Repository is authoritative - if it's not in git, it doesn't exist
2. Migration Verification System
Create a comprehensive verification system that:
- Compares schema between environments
- Detects drift before deployment
- Provides clear remediation steps
- Prevents deployment when issues are detected
3. Environments and Migration Flow
Local Development → Staging → Production
↓ ↓ ↓
(immediate) (on merge) (manual approval)
4. CI/CD Pipeline Changes
- Remove automatic migration deployment to production
- Add migration verification step that blocks on drift
- Require manual approval for production migrations
- Generate migration reports for each deployment
5. Emergency Procedures
For production-only fixes:
- Create migration file locally first
- Test in staging environment
- Apply to production via approved process
- Commit to repository immediately
Implementation
Phase 1: Migration Verification System
// scripts/verify-migrations.ts
interface MigrationStatus {
environment: string
applied: string[]
pending: string[]
missing: string[]
drift: boolean
}
async function verifyMigrations(): Promise<MigrationStatus> {
const localMigrations = await getLocalMigrations()
const remoteMigrations = await getRemoteMigrations()
return {
environment: process.env.ENVIRONMENT,
applied: remoteMigrations,
pending: localMigrations.filter((m) => !remoteMigrations.includes(m)),
missing: remoteMigrations.filter((m) => !localMigrations.includes(m)),
drift: remoteMigrations.some((m) => !localMigrations.includes(m)),
}
}
Phase 2: Migration Manifest
Create supabase/migrations/manifest.json:
{
"version": "1.0",
"migrations": [
{
"id": "20250812233110_initial_schema",
"description": "Initial database schema",
"breaking": false,
"rollback": null
},
{
"id": "20250826000001_fix_handle_new_user_error_column",
"description": "Fix error_message column in provisioning_events",
"breaking": true,
"rollback": "20250826000001_rollback.sql"
}
],
"checksum": "sha256:..."
}
Phase 3: No Staging Environment (Cost Optimization)
Decision: Skip staging environment due to cost and lack of users
- All testing done locally before commit
- Pre-commit hooks enforce local migration testing
- Production deploys require manual approval via GitHub environments
Phase 4: Simplified CI/CD Pipeline
name: Database Deployment
on:
push:
branches: [main]
paths: ['supabase/migrations/**']
jobs:
deploy-production:
runs-on: ubuntu-latest
environment: production # Requires manual approval
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to production
run: |
supabase link --project-ref ${{ secrets.SUPABASE_PROJECT_ID }}
supabase db push --include-all
- name: Deploy Edge Functions
run: |
for func in supabase/functions/*/; do
name=$(basename "$func")
[ "$name" != "_shared" ] && supabase functions deploy "$name" --no-verify-jwt
done
Key Changes:
- Removed staging environment steps
- Relies on pre-commit hooks for local testing
- GitHub environment protection provides manual approval gate
- Simplified deployment process
Phase 5: Migration Tools
Create management scripts:
migrations:verify- Check for driftmigrations:sync- Generate missing migration files from remotemigrations:report- Generate deployment reportmigrations:rollback- Apply rollback migrationmigrations:validate- Validate SQL syntax and safety
Consequences
Positive
- Eliminates schema drift between environments
- Prevents production incidents from untested migrations
- Enables safe rollbacks with versioned migrations
- Improves visibility into database state
- Enforces best practices through automation
- Maintains audit trail of all changes
Negative
- Slower deployment due to staging requirement
- Manual approval needed for production
- Initial setup complexity for staging environment
- Learning curve for new processes
Risks Mitigated
- No more "Database error saving new user" incidents
- No more emergency production fixes
- No more lost migrations
- No more inconsistent environments
Alternatives Considered
- Continue with current approach: Rejected due to ongoing incidents
- Database branching: Not supported by Supabase
- Blue-green deployments: Too complex for current needs
- GitOps with Flux/ArgoCD: Overkill for database migrations
Migration Plan
- Week 1: Implement verification system
- Week 2: Set up staging environment
- Week 3: Update CI/CD pipeline
- Week 4: Team training and documentation
- Week 5: Full cutover to new process
References
- Supabase CLI Documentation
- Database Migration Best Practices
- ADR-047: Ebisu Data Warehouse Integration
Decision Outcomes
Once implemented, we will measure success by:
- Zero migration-related production incidents
- 100% migration coverage in version control
- Deployment confidence score > 95%
- Mean time to recovery < 15 minutes