Migrations Introduction
Migrations provide version control for your database schema. Instead of manually altering tables, you define Up and Down functions that apply and revert changes. Migrations run in order and are tracked in a schema_migrations table.
Why migrations?
Migrations let you:
- Evolve the schema over time without losing data
- Share schema changes across the team via version control
- Roll back mistakes with
Down - Deploy schema updates consistently to staging and production
Creating a migration
Generate a migration with the CLI:
nimbus make:migration create_posts
This creates a timestamped file in database/migrations/. The timestamp (e.g. 20260308120000) ensures migrations run in chronological order.
Migration structure
Each migration implements database.Migration:
type Migration struct {
Name string
Up func(*gorm.DB) error
Down func(*gorm.DB) error
}
Register migrations in database/migrations/registry.go and pass them to NewMigrator.
Running migrations
migrator := database.NewMigrator(db, migrations.All())
if err := migrator.Up(); err != nil {
log.Fatalf("migration failed: %v", err)
}
Run nimbus db:migrate from your app root, or invoke go run . migrate if your main.go wires it.
Rollback
Down() reverses the last applied migration.
migrator.Down()
Best practices
- Always implement both
UpandDown - Use timestamp prefixes for deterministic ordering
- One logical change per migration
- Never edit applied migrations — add a new one instead