Migration Design
Write Reversible Migrations
Every.up.sql migration should have a corresponding .down.sql that completely reverses its changes.
Keep Migrations Small and Focused
Each migration should do one logical thing. This makes them easier to:- Review and understand
- Debug when issues occur
- Rollback independently
Use Descriptive Migration Names
Generate migrations with clear, action-oriented names:Transaction Control
Default: Use Transactions
Geni runs migrations in transactions by default, which provides:- Atomicity: All changes succeed or all fail
- Safety: Failed migrations don’t leave partial changes
- Rollback: Automatic rollback on errors
migrations/1709123456_create_tables.up.sql
When to Disable Transactions
Some operations can’t run in transactions. Add-- transaction:no as the first line:
Data Safety
Test Migrations Locally First
Always test the complete migration cycle before production:Use IF EXISTS and IF NOT EXISTS
Make migrations idempotent where possible:Safe Migration Pattern
Handle Data Migrations Carefully
When migrating data, use multiple steps:Schema Management
Version Control Schema Files
Enable schema dumping to track your database structure:schema.sql file to version control:
- Provides a snapshot of your current schema
- Helps reviewers understand structural changes
- Enables schema comparison across branches
Customize Schema File Location
Environment Management
Use Environment Variables
Store database credentials in environment variables, never in code:.env.example
Separate Configurations by Environment
Performance Optimization
Create Indexes Concurrently (PostgreSQL)
For production databases with existing data:migrations/1709123456_add_email_index.up.sql
- Doesn’t lock the table
- Allows reads and writes during creation
- Takes longer but doesn’t block production traffic
Schedule Large Migrations Carefully
For migrations that:- Modify large tables (millions of rows)
- Create indexes on large datasets
- Perform data transformations
Batch Large Data Updates
For updating many rows, batch the operations:Batch Updates
CI/CD Integration
Automated Migration Testing
Production Deployment
Deploy Workflow
Library Usage
When using Geni as a Rust library:src/main.rs
- Automatic migrations on startup
- No separate migration step in deployment
- Guaranteed schema version matches code
Common Anti-Patterns to Avoid
❌ Modifying Applied Migrations
❌ Modifying Applied Migrations
Don’t do this:
- Editing migration files that have been applied to production
- Changing timestamps of existing migrations
- Migrations are already recorded as applied
- Changes won’t be applied to existing databases
- Creates inconsistency between environments
- Create a new migration to fix issues
- Keep historical migrations as-is
❌ Deleting Migrations
❌ Deleting Migrations
Don’t do this:
- Removing migration files from the migrations folder
- Cleaning up “old” migrations
- Breaks rollback capability
- Causes errors when setting up new environments
- Loses historical context
- Keep all migrations in version control
- Use migrations as documentation of schema evolution
❌ Deploying Code Before Migrations
❌ Deploying Code Before Migrations
Don’t do this:
- Deploying application code that requires new schema
- Running migrations after the application starts
- Application errors until migrations complete
- Downtime during migration window
- Run migrations first
- Deploy backward-compatible changes
- Use feature flags for multi-step rollouts
❌ Manual Schema Changes
❌ Manual Schema Changes
Don’t do this:
- Running SQL directly in production
- “Quick fixes” that bypass migrations
- Changes not tracked in version control
- Environments drift out of sync
- Can’t reproduce or rollback
- Always create a migration
- Test it locally
- Deploy through normal process
Quick Reference
Essential Commands
Environment Variables
| Variable | Required | Default | Purpose |
|---|---|---|---|
DATABASE_URL | Yes | - | Database connection string |
DATABASE_TOKEN | No | - | LibSQL/Turso auth token |
DATABASE_MIGRATIONS_FOLDER | No | ./migrations | Migration files location |
DATABASE_MIGRATIONS_TABLE | No | schema_migrations | Tracking table name |
DATABASE_SCHEMA_FILE | No | schema.sql | Schema dump filename |
DATABASE_WAIT_TIMEOUT | No | 30 | Connection timeout (seconds) |
DATABASE_NO_DUMP_SCHEMA | No | false | Disable schema dumping |