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:

  1. Manually add columns to your local database

  2. Tell your teammate to add the same columns

  3. Hope production gets updated correctly

  4. 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 sequential

  • up method defines what changes to make

  • down method defines how to reverse those changes

  • Always 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 later

  • Think about indexes from the beginning

  • The down method should completely reverse the up method

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 posts

  • Indexes speed up common queries like "show all posts by user X"

  • published boolean lets us have draft posts

Step 4: Real-World Changes - Altering Existing Tables

Migration best practices:

  • Add fields as null: true unless absolutely required

  • Provide sensible defaults for boolean fields

  • Consider the performance impact of new fields

  • Remove indexes before dropping columns in down method


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:

  1. CQL examines your existing database structure

  2. Generates a complete schema file with all tables, columns, and relationships

  3. Creates a baseline migration state

  4. 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 definition

  • schema_name: This becomes a Crystal constant you'll use in your code

  • auto_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:

  1. Reverse in opposite order: Last change first in down method

  2. Drop foreign keys before columns: Database constraints matter

  3. Test both directions: Make sure up and then down works

  4. Don't lose data: Be careful with drop_column on 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:

  1. Communicate: Use your team chat when creating migrations

  2. Renumber: One person renames their migration to the next available number

  3. Sequential assignment: Designate one person to assign migration numbers

  4. 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



Key Takeaways

  1. Migrations are database version control - they track changes over time

  2. CQL's automatic schema generation eliminates manual synchronization errors

  3. Start simple - add complexity through migrations as your application grows

  4. Always write reversible migrations - you'll need to rollback eventually

  5. Test both directions - up and down should both work perfectly

  6. Communicate with your team - migration conflicts are easier to prevent than resolve

  7. 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?