How Migrations Work
Geni tracks applied migrations in a dedicated table (default:schema_migrations) in your database. When you run migrations, Geni compares the local migration files against the migrations recorded in the database and applies any pending migrations in chronological order.
Migration File Format
Migration files follow a strict naming convention:The timestamp is generated using Unix epoch time (seconds since January 1, 1970). This ensures migrations are ordered chronologically and prevents naming conflicts when multiple developers create migrations simultaneously.
Creating Migrations
Use thegeni new command to generate a new migration pair:
{timestamp}_create_users_table.up.sql- Apply changes{timestamp}_create_users_table.down.sql- Revert changes
Up and Down Pattern
The up/down pattern ensures that every migration can be both applied and reverted. This is crucial for:- Rolling back problematic changes in production
- Testing migrations in development
- Maintaining database history across environments
Applying Migrations
Run all pending migrations with theup command:
- Read all
.up.sqlfiles from the migrations folder - Check which migrations have already been applied
- Run pending migrations in timestamp order
- Record each migration in the
schema_migrationstable - Optionally dump the database schema
Migration Execution Process
From the source code (src/lib/migrate.rs:52-64):
Rolling Back Migrations
Revert the most recent migration:-a (amount) flag:
- Identify the last 3 applied migrations
- Run their
.down.sqlfiles in reverse chronological order (newest first) - Remove each migration record from the
schema_migrationstable
Rollback Process
From the source code (src/lib/migrate.rs:118-137):
Migration Status
Check which migrations are pending:- Total number of migrations found
- Which migrations have been applied
- Which migrations are pending
Timestamp-Based Ordering
Migrations are sorted by their timestamp prefix, ensuring they run in the order they were created:Why Timestamp-Based Instead of Sequential Numbers?
Why Timestamp-Based Instead of Sequential Numbers?
Timestamp-based migrations prevent conflicts when multiple developers create migrations on different branches:
- Sequential (problematic): Two developers both create migration
003, causing conflicts when merging - Timestamp (better): Each developer gets a unique timestamp, migrations merge cleanly and run in chronological order
Best Practices
Write Reversible Migrations
Always ensure your down migration truly reverses the up migration:Use Conditional DDL
Make migrations safer with conditional statements:Test Both Directions
Before deploying to production:Keep Migrations Small
Create focused migrations that do one thing well:Migration File Location
By default, Geni looks for migrations in./migrations. Customize this with:
Schema Migrations Table
Geni creates a tracking table (default:schema_migrations) with a simple structure:
The migrations table is automatically created the first time you run
geni up. You don’t need to create it manually.