Migrations
What Are Migrations and Why Do You Need Them?
If you've ever worked on a team project where someone says "it works on my machine" regarding database changes, you understand the pain that migrations solve. Migrations are version control for your database schema - they provide a systematic way to evolve your database structure over time while keeping everyone's environment in sync.
The Problem Migrations Solve
Imagine you're building an e-commerce application. Initially, you might have a simple users table:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(255),
email VARCHAR(255)
);Three weeks later, you need to add user preferences and email verification. Without migrations, you might:
Manually add columns to your local database
Tell your teammate to add the same columns
Hope production gets updated correctly
Lose track of what changes were made when
This approach breaks down quickly. Migrations solve this by making database changes:
Trackable: Each change is a numbered file
Reversible: You can roll back problematic changes
Shareable: Team members get the same database structure
Automatic: Your application code stays in sync with your database
How CQL Migrations Work
CQL migrations work differently from traditional SQL migrations. Instead of writing raw SQL, you use CQL's schema definition language, and the system automatically maintains a synchronized AppSchema.cr file that reflects your current database structure.
This means your Crystal code always knows the exact structure of your database, providing compile-time safety and IntelliSense support.
Core Concepts You Need to Understand
1. Migration Classes - Your Database Change Scripts
A migration class represents a single, specific change to your database structure. Think of each migration as a "diff" for your database:
Key points:
The number
(3)is the migration version - it must be unique and sequentialupmethod defines what changes to makedownmethod defines how to reverse those changesAlways test both directions!
2. The Migrator - Your Database Change Manager
The migrator is CQL's migration engine. It:
Tracks which migrations have been applied
Runs pending migrations in order
Updates your AppSchema.cr file automatically
Provides rollback capabilities
3. AppSchema.cr - Your Database's Source of Truth
This is CQL's secret sauce. Unlike other ORMs where you manually keep model definitions in sync with your database, CQL automatically generates a schema file that perfectly matches your database structure:
Why this matters:
Your Crystal code gets compile-time type safety
IntelliSense knows your database structure
No more "field not found" runtime errors
Schema changes are impossible to forget
Learning Path: From Zero to Migration Master
Step 1: Understanding the Basics (New Project)
Let's build a blog application from scratch to understand migrations:
Now you're ready to create your first migration!
Step 2: Your First Migration - Creating Tables
Key learning points:
Start with essential fields only
Always add timestamps (
created_at,updated_at) - you'll thank yourself laterThink about indexes from the beginning
The
downmethod should completely reverse theupmethod
Run this migration:
Step 3: Evolving Your Schema - Adding Relationships
Why we structure it this way:
Foreign keys maintain data integrity
on_delete: "CASCADE"means deleting a user deletes their postsIndexes speed up common queries like "show all posts by user X"
publishedboolean lets us have draft posts
Step 4: Real-World Changes - Altering Existing Tables
Migration best practices:
Add fields as
null: trueunless absolutely requiredProvide sensible defaults for boolean fields
Consider the performance impact of new fields
Remove indexes before dropping columns in
downmethod
Working with Existing Databases
The Bootstrap Process - Adopting CQL for Existing Projects
Maybe you have an existing application with an established database. CQL's bootstrap feature lets you adopt the migration workflow without starting over:
What happens during bootstrap:
CQL examines your existing database structure
Generates a complete schema file with all tables, columns, and relationships
Creates a baseline migration state
Future changes use the normal migration workflow
This is powerful because:
You don't lose any existing data
You can immediately start using CQL's type safety
Future schema changes are tracked and reversible
Team members can sync their databases easily
Configuration and Environments
Understanding MigratorConfig
Why these settings matter:
schema_file_path: This file contains your database structure definitionschema_name: This becomes a Crystal constant you'll use in your codeauto_sync: true: Schema file updates automatically (great for development)auto_sync: false: Manual control (safer for production)
Environment-Specific Configurations
Different environments need different strategies:
Why different configs for different environments:
Development: You want speed and convenience
Production: You want control and safety
Test: You want isolation and repeatability
Common Patterns and Best Practices
Migration Naming That Makes Sense
Why naming matters:
Future you (and your teammates) need to understand what each migration does
Good names make it easy to find related migrations
Clear names help when debugging or rolling back changes
Writing Reversible Migrations
Key principles for reversible migrations:
Reverse in opposite order: Last change first in
downmethodDrop foreign keys before columns: Database constraints matter
Test both directions: Make sure
upand thendownworksDon't lose data: Be careful with
drop_columnon production data
Handling Data Migrations
Sometimes you need to transform data, not just structure:
Data migration best practices:
Separate structure from data changes when possible
Batch large updates to avoid blocking the database
Document complex reversals - they're often application-specific
Test with production-like data - edge cases matter
Team Collaboration and Workflow
The Daily Development Workflow
Here's how migrations work in a team environment:
Resolving Migration Conflicts
Sometimes two developers create migrations with the same number:
How to resolve:
Communicate: Use your team chat when creating migrations
Renumber: One person renames their migration to the next available number
Sequential assignment: Designate one person to assign migration numbers
Use timestamps: Some teams use timestamp-based numbering
Handling Schema File Conflicts in Git
Debugging and Troubleshooting
Common Migration Issues and Solutions
Problem: "Column already exists"
Problem: "Cannot drop column - it's referenced by a foreign key"
Problem: "Schema file doesn't match database"
Debugging Migration Issues
Production Deployment Strategies
Safe Production Deployments
Production migrations require extra care:
Rollback Strategies
Advanced Patterns
Migration Dependencies and Ordering
Performance Considerations
Complete Example: Building a Real Application
Let's put it all together with a complete blog application:
This gives you a complete, production-ready database setup with:
User management
Post creation and publishing
Categories and relationships
Comments system
Full rollback capability
Automatic schema synchronization
Related Documentation
Initializing the Database - Database setup strategies and patterns
Altering the Schema - Detailed guide to schema modification operations
Schema Dump Guide - Working with existing databases
Integrated Migration Workflow - Advanced patterns and real-world examples
Key Takeaways
Migrations are database version control - they track changes over time
CQL's automatic schema generation eliminates manual synchronization errors
Start simple - add complexity through migrations as your application grows
Always write reversible migrations - you'll need to rollback eventually
Test both directions -
upanddownshould both work perfectlyCommunicate with your team - migration conflicts are easier to prevent than resolve
Be extra careful in production - use manual verification and rollback plans
Migrations transform database management from a painful, error-prone process into a smooth, collaborative workflow. Once you experience the safety and convenience of CQL's integrated migration system, you'll wonder how you ever managed databases without it!
Last updated
Was this helpful?