Bird
Raised Fist0
Ruby on Railsframework~15 mins

Creating migrations in Ruby on Rails - Mechanics & Internals

Choose your learning style10 modes available
Overview - Creating migrations
What is it?
Creating migrations in Rails means writing special files that tell the database how to change its structure. These files describe adding or removing tables, columns, or indexes in a way that Rails can understand and apply step-by-step. Migrations help keep the database organized and in sync with the code. They are like instructions for the database to evolve safely over time.
Why it matters
Without migrations, changing the database structure would be risky and confusing, especially when working with a team. Migrations solve this by tracking every change in a clear, repeatable way. This prevents mistakes like losing data or having different database versions on different computers. It makes updating and sharing the database structure easy and reliable.
Where it fits
Before learning migrations, you should understand basic Ruby and Rails models, and how databases store data. After migrations, you can learn about advanced database features like indexing, constraints, and schema design. Migrations fit into the bigger picture of managing data and application growth.
Mental Model
Core Idea
Migrations are step-by-step instructions that safely change the database structure over time.
Think of it like...
Imagine building a LEGO model where each migration is a new instruction sheet telling you which pieces to add or remove, so the model grows exactly as planned without breaking.
┌───────────────┐
│ Migration 001 │
│ Create users  │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Migration 002 │
│ Add email to  │
│ users table   │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Migration 003 │
│ Create posts  │
└───────────────┘

Each migration builds on the last, changing the database step-by-step.
Build-Up - 7 Steps
1
FoundationWhat is a migration file
🤔
Concept: A migration file is a Ruby script that describes a change to the database structure.
In Rails, migrations live in the db/migrate folder. Each migration has a timestamp and a descriptive name, like 20240610123456_create_users.rb. Inside, it has methods called up and down or a change method that tells Rails how to apply or undo the change. For example, creating a users table with columns for name and email.
Result
You get a clear, versioned file that Rails can run to update the database schema.
Understanding that migrations are just Ruby files helps you see they are code you can read, write, and control like any other part of your app.
2
FoundationRunning migrations with Rails commands
🤔
Concept: Rails provides commands to apply or revert migrations safely on your database.
You run migrations with `rails db:migrate` to apply all pending changes. To undo the last migration, use `rails db:rollback`. These commands update a special table called schema_migrations that tracks which migrations have run. This keeps your database and code in sync.
Result
The database structure changes as described by the migration files, and Rails knows which changes are done.
Knowing how to run and rollback migrations is key to safely evolving your database without losing data or causing errors.
3
IntermediateWriting change method for reversible migrations
🤔Before reading on: do you think all migrations need separate up and down methods, or can one method handle both?
Concept: The change method lets Rails automatically reverse migrations when possible, simplifying code.
Instead of writing separate up and down methods, you can write a single change method. For example, `create_table :users do |t| t.string :name end` inside change lets Rails know how to create and drop the table automatically. This reduces errors and makes migrations easier to maintain.
Result
Migrations become shorter and Rails can rollback changes without extra code.
Understanding reversible migrations saves time and prevents mistakes when undoing database changes.
4
IntermediateAdding columns and indexes safely
🤔Before reading on: do you think adding a column with a default value affects existing rows immediately or only new rows?
Concept: Migrations can add columns and indexes with options that affect performance and data safety.
You can add columns with `add_column :table, :column, :type, default: value, null: false`. Adding indexes with `add_index :table, :column` speeds up queries. But adding a column with a default and not null can lock the table during migration, so sometimes you add the column nullable first, fill data, then change constraints.
Result
The database structure improves with new columns and faster queries, but you must be careful to avoid downtime.
Knowing how to add columns and indexes properly helps keep your app fast and available during migrations.
5
IntermediateUsing migration generators for consistency
🤔
Concept: Rails provides generators to create migration files with correct naming and structure automatically.
Instead of writing migration files from scratch, use commands like `rails generate migration AddAgeToUsers age:integer`. This creates a migration file with the right timestamp and a change method adding the age column. Generators reduce errors and keep your migrations organized.
Result
You get ready-to-edit migration files that follow Rails conventions.
Using generators speeds up development and ensures your migrations follow best practices.
6
AdvancedManaging complex migrations in production
🤔Before reading on: do you think running large migrations always happens instantly without affecting users?
Concept: Large or complex migrations require careful planning to avoid downtime or data loss in live apps.
In production, migrations that lock tables or take long can cause app slowdowns or errors. Techniques like breaking migrations into smaller steps, adding columns nullable first, backfilling data in batches, and then adding constraints help. Also, avoid dropping columns immediately; instead, deprecate them first.
Result
Database changes happen smoothly without interrupting users or causing errors.
Understanding production migration strategies prevents costly downtime and keeps your app reliable.
7
ExpertHow Rails tracks and applies migrations internally
🤔Before reading on: do you think Rails runs all migration files every time or only the new ones?
Concept: Rails uses a special table to track which migrations have run and applies only new ones in order.
Rails stores applied migration versions in the schema_migrations table. When you run `rails db:migrate`, Rails compares migration files in db/migrate with this table and runs only those not recorded yet. This ensures migrations run once and in the correct order. The schema.rb file is updated to reflect the current database structure.
Result
Migrations run safely and only once, keeping database and code aligned.
Knowing how Rails tracks migrations helps debug issues and understand migration failures.
Under the Hood
When you run a migration, Rails loads the migration files in timestamp order. It checks the schema_migrations table to see which versions have already run. For each new migration, Rails executes the change, up, or down methods inside a database transaction if supported. After successful execution, it records the migration version in schema_migrations. This process ensures migrations are atomic and ordered.
Why designed this way?
Rails migrations were designed to provide a simple, code-driven way to evolve databases safely. Tracking applied migrations prevents duplicate changes and conflicts. Using Ruby code for migrations leverages the language's expressiveness and integrates tightly with Rails models. Alternatives like manual SQL scripts were error-prone and hard to manage across teams.
┌─────────────────────────────┐
│ db/migrate folder            │
│ ┌───────────────┐           │
│ │ Migration 001 │           │
│ └───────────────┘           │
│ ┌───────────────┐           │
│ │ Migration 002 │           │
│ └───────────────┘           │
└─────────────┬───────────────┘
              │
              ▼
┌─────────────────────────────┐
│ schema_migrations table      │
│ ┌───────────────┐           │
│ │ Version 001   │◄──────────┤
│ └───────────────┘           │
│ ┌───────────────┐           │
│ │ Version 002   │◄──────────┤
│ └───────────────┘           │
└─────────────┬───────────────┘
              │
              ▼
┌─────────────────────────────┐
│ Database schema updated      │
│ Tables, columns, indexes     │
└─────────────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: do you think running the same migration file twice will cause errors or is it safe?
Common Belief:Many believe migrations can be run multiple times safely without issues.
Tap to reveal reality
Reality:Rails runs each migration only once by tracking versions; rerunning the same migration manually causes errors or duplicate changes.
Why it matters:Not understanding this can lead to broken databases or failed deployments when migrations are applied incorrectly.
Quick: do you think migrations automatically update your Rails models to match the database?
Common Belief:Some think migrations change the Rails model code automatically.
Tap to reveal reality
Reality:Migrations only change the database structure; Rails models must be updated manually to reflect schema changes.
Why it matters:Assuming automatic updates can cause bugs where the app expects columns or tables that don't exist in code.
Quick: do you think adding a column with a default value always happens instantly without locking the table?
Common Belief:Many believe adding columns with defaults is always fast and safe in production.
Tap to reveal reality
Reality:Adding columns with defaults and NOT NULL constraints can lock large tables and cause downtime unless done carefully in steps.
Why it matters:Ignoring this can cause app outages during migrations on busy production databases.
Quick: do you think the order of migration files does not matter as long as all are applied?
Common Belief:Some believe migration order is unimportant if all migrations run eventually.
Tap to reveal reality
Reality:Migration order is critical; running them out of order can cause missing tables or columns and break the app.
Why it matters:Misordering migrations leads to errors and inconsistent database states.
Expert Zone
1
Migrations can be written to be idempotent, allowing safe re-runs in complex deployment scenarios.
2
Using raw SQL inside migrations is sometimes necessary for performance or features but requires careful rollback handling.
3
Schema.rb is a snapshot of the current database schema, but structure.sql is preferred for complex database features like triggers or views.
When NOT to use
Migrations are not suitable for large data transformations or cleanup tasks; use background jobs or scripts instead. Also, avoid using migrations for frequent small changes in production without planning, as they can cause downtime. For non-Rails databases or legacy systems, other schema management tools might be better.
Production Patterns
In production, teams use zero-downtime migration patterns like adding nullable columns first, backfilling data asynchronously, and then adding constraints. They also review migrations carefully, test on staging, and use feature flags to deploy code that depends on new schema changes safely.
Connections
Version Control Systems
Migrations build on the idea of version control by tracking database changes over time.
Understanding how migrations track changes like Git tracks code helps grasp why migrations prevent conflicts and keep teams synchronized.
Database Transactions
Migrations often run inside database transactions to ensure atomic changes.
Knowing how transactions work explains why migrations either fully apply or fully rollback, keeping the database consistent.
Project Management Change Logs
Migrations act like a change log for the database schema, documenting every structural update.
Seeing migrations as a formal record of changes helps appreciate their role in team communication and auditing.
Common Pitfalls
#1Running migrations without checking for pending schema changes causes conflicts.
Wrong approach:rails db:migrate # runs without verifying if schema.rb is updated or conflicts exist
Correct approach:git pull rails db:migrate # ensures code and migrations are up to date before running
Root cause:Not syncing code and database changes leads to migration conflicts and errors.
#2Adding a NOT NULL column with a default in one step on a large table causes downtime.
Wrong approach:add_column :users, :age, :integer, null: false, default: 0
Correct approach:add_column :users, :age, :integer User.update_all(age: 0) change_column_null :users, :age, false
Root cause:Misunderstanding how database engines handle defaults and constraints causes table locks.
#3Deleting columns immediately after removing code references causes data loss.
Wrong approach:remove_column :users, :old_field
Correct approach:# First remove code references, deploy, then remove column in a later migration
Root cause:Not separating code and schema changes leads to app errors or lost data.
Key Takeaways
Migrations are Ruby files that describe step-by-step changes to the database structure.
Rails tracks which migrations have run to apply changes safely and in order.
Using the change method allows reversible migrations that Rails can rollback automatically.
Careful planning of migrations in production avoids downtime and data loss.
Migrations do not update Rails models automatically; both schema and code must stay in sync.

Practice

(1/5)
1. What is the main purpose of a migration in Rails?
easy
A. To change the database structure step-by-step
B. To write application business logic
C. To style the user interface
D. To manage user sessions

Solution

  1. Step 1: Understand what migrations do

    Migrations are used to modify the database schema in a controlled way.
  2. Step 2: Identify the correct purpose

    Changing database structure step-by-step matches the role of migrations.
  3. Final Answer:

    To change the database structure step-by-step -> Option A
  4. Quick Check:

    Migrations = Change database structure [OK]
Hint: Migrations change database structure, not app logic or UI [OK]
Common Mistakes:
  • Confusing migrations with application code
  • Thinking migrations handle UI or sessions
2. Which command correctly generates a migration to add a title column of type string to the posts table?
easy
A. rails generate migration AddColumn posts title:string
B. rails generate migration CreatePosts title:string
C. rails generate migration AddTitleToPosts title:string
D. rails generate migration AddTitle posts string

Solution

  1. Step 1: Identify the correct migration naming pattern

    Adding a column uses 'AddColumnNameToTableName' format, here 'AddTitleToPosts'.
  2. Step 2: Check the command syntax

    The correct syntax is 'rails generate migration AddTitleToPosts title:string'.
  3. Final Answer:

    rails generate migration AddTitleToPosts title:string -> Option C
  4. Quick Check:

    Correct migration command = rails generate migration AddTitleToPosts title:string [OK]
Hint: Use 'AddColumnToTable' naming with column:type [OK]
Common Mistakes:
  • Using 'CreatePosts' instead of 'AddTitleToPosts'
  • Wrong order or missing 'To' in migration name
  • Incorrect column type syntax
3. Given this migration code:
class AddAgeToUsers < ActiveRecord::Migration[7.0]
  def change
    add_column :users, :age, :integer
  end
end

What happens after running rails db:migrate?
medium
A. Raises a syntax error due to missing migration version
B. Removes the 'age' column from the 'users' table
C. Creates a new 'users' table with an 'age' column
D. Adds an integer column named 'age' to the 'users' table

Solution

  1. Step 1: Analyze the migration class

    The migration inherits from ActiveRecord::Migration[7.0] and defines a change method adding a column.
  2. Step 2: Understand the add_column method

    It adds an integer column 'age' to the existing 'users' table.
  3. Final Answer:

    Adds an integer column named 'age' to the 'users' table -> Option D
  4. Quick Check:

    add_column adds column = Adds an integer column named 'age' to the 'users' table [OK]
Hint: add_column adds a new column to existing table [OK]
Common Mistakes:
  • Confusing add_column with remove_column
  • Thinking it creates a new table
  • Missing migration version causes error (not here)
4. You wrote this migration:
class AddEmailToUsers < ActiveRecord::Migration[7.0]
  def change
    add_column :users, :email
  end
end

What is the problem when running rails db:migrate?
medium
A. Syntax error due to missing 'def up' method
B. Missing column type for 'email' causes an error
C. Migration runs successfully without issues
D. Table 'users' does not exist error

Solution

  1. Step 1: Check add_column method usage

    add_column requires three arguments: table name, column name, and column type.
  2. Step 2: Identify missing argument

    The migration misses the column type for 'email', causing an error.
  3. Final Answer:

    Missing column type for 'email' causes an error -> Option B
  4. Quick Check:

    add_column needs type argument [OK]
Hint: Always specify column type with add_column [OK]
Common Mistakes:
  • Omitting column type argument
  • Assuming migration runs without errors
  • Confusing change method with up/down methods
5. You want to create a migration that renames the column username to login in the accounts table. Which migration code correctly does this?
hard
A. class RenameUsernameToLogin < ActiveRecord::Migration[7.0] def change rename_column :accounts, :username, :login end end
B. class RenameUsernameToLogin < ActiveRecord::Migration[7.0] def change change_column :accounts, :username, :login end end
C. class RenameUsernameToLogin < ActiveRecord::Migration[7.0] def change rename_table :username, :login end end
D. class RenameUsernameToLogin < ActiveRecord::Migration[7.0] def change remove_column :accounts, :username add_column :accounts, :login, :string end end

Solution

  1. Step 1: Identify the correct method to rename a column

    Rails provides rename_column to rename columns in a table.
  2. Step 2: Check each option's method

    class RenameUsernameToLogin < ActiveRecord::Migration[7.0] def change rename_column :accounts, :username, :login end end uses rename_column correctly; class RenameUsernameToLogin < ActiveRecord::Migration[7.0] def change change_column :accounts, :username, :login end end wrongly uses change_column; class RenameUsernameToLogin < ActiveRecord::Migration[7.0] def change rename_table :username, :login end end tries to rename tables; class RenameUsernameToLogin < ActiveRecord::Migration[7.0] def change remove_column :accounts, :username add_column :accounts, :login, :string end end removes and adds columns instead of renaming.
  3. Final Answer:

    Use rename_column :accounts, :username, :login in change method -> Option A
  4. Quick Check:

    rename_column renames columns = class RenameUsernameToLogin < ActiveRecord::Migration[7.0] def change rename_column :accounts, :username, :login end end [OK]
Hint: Use rename_column to rename columns, not change_column [OK]
Common Mistakes:
  • Using change_column instead of rename_column
  • Trying to rename tables instead of columns
  • Removing and adding columns instead of renaming
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard