Appearance
Deployment Strategy โ
Overview โ
Zeron uses a multi-stage deployment strategy to ensure stability and enable safe testing before production releases.
Branch-Based Deployment Model โ
๐ Production Branch โ
- Purpose: Stable production releases only
- Deployment: Manual promotion after validation
- Protection: Strict branch protection rules, required reviews
- Environment: Full production configuration
๐งช Staging Branch โ
- Purpose: Integration testing and pre-production validation
- Deployment: Automatic on merge
- Source: Merge target for all feature branches
- Environment: Staging configuration with isolated resources
๐ง Development Branch โ
- Purpose: Continuous integration for active development
- Deployment: Local/development only
- Function: Integration point for feature development
- Environment: Local development configuration
Deployment Workflow โ
Feature Branches โ Staging โ Production
โ โ โ
Development Testing Live
(Local) (Validation) (Customers)Development Workflow โ
1. Feature Development โ
- Start from staging branch
- Create isolated feature branch
- Develop and test locally
- Submit PR targeting staging
2. Staging Validation โ
- Automatic deployment to staging environment
- Integration testing with other systems
- QA validation and testing
- Performance verification
3. Production Deployment โ
- Manual promotion after staging validation
- Controlled rollout with monitoring
- Rollback capability maintained
- Health checks and verification
Environment Configuration โ
Environment Separation โ
- Production: Full production credentials and resources
- Staging: Isolated staging resources (separate databases, APIs)
- Development: Local development configuration
Database Migration Workflow โ
Managing database schema changes across environments is critical for maintaining stability and preventing data loss. This section outlines the standardized approach for database migrations in Zeron applications.
Three-Environment Database Architecture โ
CRITICAL: Each environment MUST use a completely separate database instance:
Local Development (Docker/Local)
- Purpose: Development and testing of schema changes
- Database: Local PostgreSQL via Docker Compose
- Connection: Local connection string (never committed to Git)
- Migrations: Created and tested here first
Staging (Cloud Database)
- Purpose: Integration testing with production-like data
- Database: Separate cloud database instance (e.g., Prisma Cloud, Neon, RDS)
- Connection: Staging-specific connection string (stored in Vercel/deployment platform)
- Migrations: Applied manually after staging deployment
Production (Cloud Database)
- Purpose: Live customer data
- Database: Separate production cloud database instance
- Connection: Production-specific connection string (stored in Vercel/deployment platform)
- Migrations: Applied manually after production deployment
Why Separate Databases?
- Multi-tenancy isolation: Prevents cross-environment data leaks
- Safe testing: Staging failures don't affect production
- Realistic testing: Staging can use production-like data volumes
- Security: Production credentials never touch development environments
Migration Workflow Principles โ
Manual Migrations Are Intentional
Database migrations MUST be run manually after code deployment, not automatically. This is a safety feature:
- โ Allows verification before applying schema changes
- โ Prevents cascading failures from bad migrations
- โ Enables emergency rollback without re-deploying code
- โ Provides clear audit trail of when migrations ran
- โ Automatic migrations can cause downtime during deployment
- โ Failed migrations can break entire deployment process
Never Use Development Migration Commands in Cloud
For Prisma-based projects:
- โ
migrate dev- Only for local development (creates AND applies migrations) - โ
migrate deploy- For staging/production (applies existing migrations only) - โ
migrate reset- NEVER in cloud (destructive, deletes all data)
Golden Rules
- Migrations are created and tested locally FIRST
- Migrations are committed to Git alongside code changes
- Code deploys automatically (via branch strategy)
- Migrations deploy manually (via CLI commands)
- Always verify migration status before running migrations
Standard Migration Workflow โ
Step 1: Create Migration Locally โ
bash
# Create new migration
npm run db:migrate:create add_user_preferences
# Edit the generated migration SQL if needed
# Apply migration to local database
npm run db:migrate:dev
# Test your changes thoroughly
npm run devWhat's Happening:
- Prisma generates migration SQL based on schema.prisma changes
- Migration is applied to your local Docker database
- Migration files are created in
prisma/migrations/directory - You verify the changes work correctly
Step 2: Commit and Push to Staging โ
bash
# Commit migration files with your code changes
git add prisma/migrations/
git add prisma/schema.prisma
git add src/ # Your code changes
git commit -m "feat: Add user preferences table (ZER-123)"
# Push to staging branch
git push origin stagingWhat's Happening:
- Migration files are now in version control
- Vercel/deployment platform automatically deploys your code to staging
- Database schema has NOT changed yet (code deployment โ migration deployment)
Step 3: Apply Migrations to Staging โ
bash
# Manually run migrations against staging database
npm run db:migrate:staging
# Verify migrations applied successfully
npm run db:migrate:staging:statusWhat's Happening:
- Command reads
.env.stagingfor database connection string - Prisma applies pending migrations to staging database
- Schema changes are now live in staging environment
- Your code can now interact with the new schema
Step 4: Test Staging (24-48 Hour Soak) โ
bash
# Create test data if needed
npm run db:test-data:create
# Test thoroughly at staging URL
# - Integration tests
# - Manual QA
# - Performance verification
# - Edge cases
# Clean up test data when done
npm run db:test-data:cleanupWhat's Happening:
- Staging environment runs with new schema for 24-48 hours
- Catch issues before they reach production
- Verify migrations work with production-like data volumes
- Test rollback procedures if needed
Step 5: Deploy to Production โ
bash
# Merge staging into production branch
git checkout master
git merge staging
git push origin master
# Vercel/deployment platform automatically deploys code
# Wait for deployment to complete...
# Manually run migrations against production database
npm run db:migrate:production
# Verify migrations applied successfully
npm run db:migrate:production:statusWhat's Happening:
- Production code is now deployed (but using old schema)
- You manually apply migrations when ready
- Schema changes are now live in production
- Monitor for issues immediately after migration
Essential Migration Scripts โ
Every Zeron application with a database should implement these scripts in package.json:
Local Development Scripts โ
json
{
"db:migrate:create": "Create new migration without applying",
"db:migrate:dev": "Create and apply migration locally",
"db:migrate:reset": "Reset local database (DESTRUCTIVE)",
"db:migrate:status": "Check local migration status",
"db:backup": "Backup local database before risky operations"
}Staging Environment Scripts โ
json
{
"db:migrate:staging": "Apply migrations to staging database",
"db:migrate:staging:status": "Check staging migration status",
"db:test-data:create": "Generate test data for staging",
"db:test-data:cleanup": "Remove all test data from staging"
}Production Environment Scripts โ
json
{
"db:migrate:production": "Apply migrations to production database",
"db:migrate:production:status": "Check production migration status"
}Utility Scripts โ
json
{
"db:verify": "Pre-deployment verification (CI/CD)",
"db:migrate:rollback": "Interactive emergency rollback tool"
}Implementation Notes:
- Use
dotenv-clito load environment-specific connection strings - Staging scripts read from
.env.staging(never committed) - Production scripts read from
.env.production(never committed) - Example:
"db:migrate:staging": "dotenv -e .env.staging -- prisma migrate deploy"
Handling Destructive Changes โ
Dropping columns or tables requires a 3-phase approach to prevent downtime and data loss:
Phase 1: Make Column Nullable (Migration 1) โ
typescript
// Update schema.prisma
model User {
id String @id
oldField String? // Add ? to make nullable
}Actions:
- Create migration:
npm run db:migrate:create make_old_field_nullable - Deploy to staging โ test โ deploy to production
- Wait for full deployment cycle (code in staging and production)
Why: Old code can still write to the field, new code ignores it
Phase 2: Remove Code References (No Migration) โ
typescript
// Remove all code that uses oldField
model User {
id String @id
oldField String? // Still in schema, but no code uses it
}Actions:
- Remove code references in your application
- Deploy to staging โ test โ deploy to production
- Verify no code accesses the field anymore
Why: Code is ready for field to disappear
Phase 3: Drop Column (Migration 2) โ
typescript
// Update schema.prisma - remove field entirely
model User {
id String @id
// oldField is gone
}Actions:
- Create migration:
npm run db:migrate:create remove_old_field - Deploy to staging โ test โ deploy to production
- Field is permanently deleted
Why: Safe to drop now - no code references it
Benefits of 3-Phase Approach:
- โ Zero downtime during migration
- โ Safe rollback at any phase
- โ Old and new code can coexist temporarily
- โ No race conditions between deployment and migration
Migration Rollback Procedures โ
Emergency Rollback (Critical Production Issues) โ
Revert Code Deployment
- Use Vercel/deployment platform dashboard
- Redeploy previous working version
- Restores old code immediately (schema still changed)
Mark Migration as Rolled Back
- Run:
npm run db:migrate:rollback - Follow interactive prompts
- Mark the problematic migration as "rolled back"
- Run:
Create Reversal Migration
- Run:
npm run db:migrate:create reversal_of_problematic_change - Manually edit SQL to undo schema changes
- Example:
ALTER TABLE "users" DROP COLUMN "new_field";
- Run:
Test Reversal in Staging
- Deploy reversal migration to staging first
- Verify old code works with reverted schema
- Test thoroughly before production
Apply to Production
- Merge to production branch
- Run:
npm run db:migrate:production - Monitor closely
Important: Migration rollback does NOT automatically undo schema changes. You must create a new migration that reverses the changes manually.
Planned Rollback (Testing/Development) โ
For local development only:
bash
# Reset entire local database
npm run db:migrate:reset
# Restore from backup
npm run db:backup:restore <backup_file>Never use migrate reset in staging or production!
Test Data Management โ
Creating Test Data (Staging Only) โ
bash
# Generate realistic test data in staging
npm run db:test-data:createWhat It Should Create:
- Multiple test accounts with different plan types
- Sample data for integration testing
- Edge cases (empty states, maximum limits)
- All records marked as
isDemoAccount: trueor similar flag
Safety Features:
- Production detection: Refuse to run if production database detected
- Clear labeling: All test data clearly marked as demo/test
- Easy cleanup: Simple command to remove all test data
Cleaning Up Test Data โ
bash
# Remove all demo/test accounts and related data
npm run db:test-data:cleanupImplementation:
- Query for all records with
isDemoAccount: true - Cascade delete all related data
- Require confirmation: User types "DELETE" to proceed
- Never touches real user data
Production Rule โ
NEVER seed or generate data in production!
- โ No test accounts in production
- โ No demo data in production
- โ No seed scripts in production
- โ Production only receives real user data
- โ Test everything in staging first
Best Practices for Database Migrations โ
DO These Things โ
Always Create Migrations Locally First
- Never create migrations directly in cloud environments
- Test locally with Docker before committing
- Verify migration SQL is correct
Use Descriptive Migration Names
bashโ npm run db:migrate:create add_user_preferences_table โ npm run db:migrate:create update_order_status_enum โ npm run db:migrate:create add_supplier_email_index โ npm run db:migrate:create changes โ npm run db:migrate:create fixTest in Staging for 24-48 Hours
- Don't rush to production
- Catch performance issues with production-like data volumes
- Verify migrations work under load
Commit Migration Files to Git
- All files in
prisma/migrations/directory - Updated
prisma/schema.prisma - Migration lock file (ensures same database provider)
- Never edit migration files after committing
- All files in
Verify Before Deploying
bash# Check migration status before production deployment npm run db:verify npm run db:migrate:statusBackup Before Risky Operations
bash# Local database backup npm run db:backup # Cloud databases have automatic backups # Check your cloud provider dashboardUse Migration Lock Files
- Commit
prisma/migrations/migration_lock.toml - Prevents accidental database provider mismatches
- Ensures PostgreSQL in all environments
- Commit
DON'T Do These Things โ
Never Use
migrate devin Cloud Environments- It creates AND applies migrations (dangerous in production)
- Use
migrate deployfor cloud environments
Never Edit Migration Files After Committing
- Migration history must be immutable
- Create new migration to fix issues
Never Skip Staging
- Direct to production is dangerous
- Staging catches 90% of migration issues
Never Auto-Run Migrations on Deployment
- Manual migrations provide safety and control
- Bad migrations shouldn't block code deployment
Never Commit
.envFiles with Credentials- Database connection strings stay in deployment platform
- Use
.env.examplefor templates only
Never Drop Columns/Tables Immediately
- Use 3-phase approach (see above)
- Prevents downtime and data loss
Never Fallback to Default Data
- If shopId/userId/tenantId is missing, fail loudly
- Don't silently use sample data
- Prevents data leaks in multi-tenant systems
Integration with Branch Strategy โ
Database migrations follow the same branch-based deployment model:
Feature Branch (Local)
โโ Create migration: db:migrate:create
โโ Apply locally: db:migrate:dev
โโ Test changes
โโ Commit migration files
โ
Staging Branch (Cloud)
โโ Code auto-deploys (Vercel)
โโ Manually run: db:migrate:staging
โโ Test for 24-48 hours
โโ Verify with test data
โ
Production Branch (Cloud)
โโ Code auto-deploys (Vercel)
โโ Manually run: db:migrate:production
โโ Monitor closely
โโ Maintain rollback capabilityKey Timing:
- Code deployment: Automatic (branch strategy handles this)
- Migration deployment: Manual (you run the command)
- Gap between deployment and migration: Usually minutes, but you control timing
When to Run Migrations:
- โ After Vercel deployment completes
- โ During low-traffic periods (if possible)
- โ When you can monitor immediately after
- โ Not automatically as part of build process
- โ Not during high-traffic periods (if avoidable)
CI/CD Integration โ
Add migration verification to your CI pipeline:
yaml
# .github/workflows/ci.yml
- name: Setup test database
run: |
docker run -d --name test-postgres \
-e POSTGRES_PASSWORD=test \
-e POSTGRES_DB=app_test \
-p 5433:5432 postgres:16-alpine
- name: Verify migrations
run: npm run db:verify
env:
DATABASE_URL: postgresql://postgres:test@localhost:5433/app_test
- name: Run migrations
run: npx prisma migrate deploy
env:
DATABASE_URL: postgresql://postgres:test@localhost:5433/app_testBenefits:
- Catches migration issues before they reach staging
- Verifies migrations apply cleanly to fresh database
- Tests that schema matches Prisma client generation
- Fails PR if migrations are broken
Quick Reference Checklist โ
Setting Up New Zeron App:
- [ ] Create three separate database instances (local, staging, production)
- [ ] Configure connection strings in deployment platform (never commit)
- [ ] Set up Docker Compose with PostgreSQL for local development
- [ ] Add migration scripts to package.json
- [ ] Install dotenv-cli for environment-specific commands
- [ ] Create migration lock file (commit to Git)
- [ ] Add migration verification to CI/CD pipeline
- [ ] Document environment setup in project README
For Each Migration:
- [ ] Create migration locally:
npm run db:migrate:create - [ ] Apply to local database:
npm run db:migrate:dev - [ ] Test changes thoroughly locally
- [ ] Commit migration files with code changes
- [ ] Push to staging branch (code auto-deploys)
- [ ] Apply to staging database:
npm run db:migrate:staging - [ ] Test in staging for 24-48 hours
- [ ] Merge to production branch (code auto-deploys)
- [ ] Apply to production database:
npm run db:migrate:production - [ ] Monitor production closely after migration
Red Flags (Stop Immediately):
- โ Migration modifying production data unexpectedly
- โ Migration taking longer than expected (>30 seconds)
- โ Migration errors in logs
- โ Application errors immediately after migration
- โ Performance degradation after migration
If Red Flags Appear:
- Do NOT continue with deployment
- Rollback code deployment via platform dashboard
- Create reversal migration
- Test reversal in staging
- Investigate root cause before retrying
Benefits โ
- โ Controlled releases: No accidental production updates
- โ Safe testing: Validate changes before customer impact
- โ Rollback capability: Easy revert when issues arise
- โ Parallel development: Multiple features tested simultaneously
- โ Security: Production environment fully protected
- โ Confidence: Thorough validation before release
Best Practices โ
Branch Protection โ
- Require reviews before merging
- Enforce passing tests and checks
- Restrict direct pushes to critical branches
- Maintain clean commit history
Deployment Safety โ
- Always deploy to staging first
- Monitor staging for issues
- Use feature flags for gradual rollouts
- Document rollback procedures
Rollback Strategy โ
Emergency Rollback โ
Quick revert to last known good state when critical issues arise:
- Identify last stable deployment
- Revert production to that commit
- Verify system health
- Investigate root cause
Planned Rollback โ
Controlled rollback for identified issues:
- Revert specific changes
- Test in staging first
- Deploy rollback during maintenance window
- Communicate with stakeholders
Key Principles โ
- Never skip staging - All changes go through staging first
- Automate validation - Use automated checks wherever possible
- Monitor actively - Watch metrics after every deployment
- Document everything - Maintain clear deployment history
- Test rollbacks - Ensure rollback procedures actually work
