ActiveRecord Migrations Basics
Fast recall
Quick revision
Core answer
bin/rails db:migrate applies pending versions and records them; reversible operations in change can be rolled back, while complex changes use explicit up and down. Once a migration has run in shared environments, create a new corrective migration instead of editing history. The schema dump records current structure, while migrations record how it evolved.Whether you understand a migration as a versioned, reversible schema change (change method, db:migrate, db:rollback) that keeps the team's DBs in sync.
Editing a migration that's already been run (others have it; use a new migration instead), or forgetting db:rollback reversibility.
A migration is a versioned, reversible schema change (create table/add column/index). Generate, db:migrate to apply, db:rollback to undo. Evolve the schema incrementally.
Interview-ready guide
Interview answer
An Active Record migration is a timestamped Ruby class that describes one database schema change. Rails runs pending versions in timestamp order and records which versions succeeded. That shared history lets development, test, and production databases move toward the same structure from their current version.
Put operations in change when Rails has enough information to reverse them. Use explicit up and down when applying and undoing require different instructions. Removing data or running arbitrary SQL may have no honest inverse, even when the Ruby syntax itself is valid.
A useful migration defines more than column names. A reference may need null: false and foreign_key: true; lookup or identity columns may need indexes. The example creates invoices with their account relationship and unique invoice number so the database can enforce those rules for every writer.
bin/rails db:migrate runs pending files, records their versions, and updates the configured schema dump. Migrations explain how the database changed; db/schema.rb or structure.sql describes its current shape. The database itself remains the source of truth for the structure that is actually running.
After a migration reaches teammates or production, fix it with a new migration instead of editing the old file; applied databases would not rerun that version. For large tables, reversibility is not enough. Add compatible structure, deploy code, backfill safely, enforce constraints, and remove the old shape in later steps.
ActiveRecord Migrations Basics
Direct Answer
An Active Record migration is a timestamped Ruby class that describes a database schema change, such as creating a table, adding a column, index, constraint, or foreign key. bin/rails db:migrate applies pending versions and records them; reversible operations in change can be rolled back, while complex changes use explicit up and down. Once a migration has run in shared environments, create a new corrective migration instead of editing history. The schema dump records current structure, while migrations record how it evolved.
Deep Dive & Architecture
Rails compares timestamp versions in the migration directory with versions stored in the database's schema migration metadata. Pending files run in order. This mechanism coordinates schema state; it does not compare the Ruby source of a migration that was already marked complete.
Reversibility is operation-specific. Rails can invert common changes such as creating a table or adding a column when enough information is present. Removing data, executing arbitrary SQL, or transforming values may not have a truthful automatic inverse, so the author must supply one or mark the operation irreversible.
The schema dump represents the current shape used for inspection and loading a fresh database. It is not a replacement for migration review and should not be manually used as a hidden migration. Both the migration and resulting dump normally belong in version control.
Large production changes need staged compatibility. A new nullable column may be deployed before application code writes it, data can be backfilled in controlled batches, then constraints can be tightened later. This allows old and new application processes to operate during a rolling deploy without long locks or invalid reads.
Code Example
class CreateInvoices < ActiveRecord::Migration[8.1]
def change
create_table :invoices do |t|
t.string :number, null: false
t.references :account, null: false, foreign_key: true
t.integer :total_cents, null: false
t.timestamps
end
add_index :invoices, :number, unique: true
end
end
Common Mistakes & Pitfalls
Editing a migration that's already been run (others have it; use a new migration instead), or forgetting db:rollback reversibility.
Check your understanding
Answer these without looking back at the article.
- What makes a `change` migration reversible?
- Why should an already-deployed migration not be edited?
- How does a migration differ from `schema.rb` or `structure.sql`?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.