RL ROLAND LOPEZ
// 1 min read

Rails Migration Best tips

1. Never modify the up of a committed migration

Editing the up of a committed migration invites chaos in your deploys and team sync.

# db/migrate/20230101120000_example_migration.rb
class ExampleMigration < ActiveRecord::Migration[6.0]
  def up
    add_column :users, :email, :string, null: false
  end

  def down
    remove_column :users, :email
  end
end

Once it is committed, leave up alone, or you force teammates into error-prone database version syncs. Write a new migration instead.

2. No application code in a migration

Ever joined a team where a migration would not run because of some error in a model?

Calling model classes or other Ruby inside a migration creates migrations that break later, as the code around them changes.

In plain terms:

  • Day 1: you add Post.create(title: "Migration Best Practices") in your migration.
  • Day 2: another dev adds validates :content, presence: true to the Post model.
  • Day 3: a new hire cannot run your day-1 migration, because of that validation.

What now, edit a committed migration?

The fix: run raw SQL. No ORM in migrations.

# db/migrate/20230101121000_add_jobs_count_to_user.rb
class AddJobsCountToUser < ActiveRecord::Migration[6.0]
  def up
    add_column :users, :jobs_count, :integer, default: 0
    # Poor approach: depend on the User model which might change
    # Good approach: use pure SQL for reliability
    execute <<-SQL
      UPDATE users
      SET jobs_count = (SELECT count(*) FROM jobs WHERE jobs.user_id = users.id)
    SQL
  end

  def down
    remove_column :users, :jobs_count
  end
end

SQL keeps the migration decoupled from your evolving business logic.

3. Always provide a down

Every migration should be undoable. If a change truly cannot be reversed, raise an exception so it is explicit.

# db/migrate/20230101121500_create_users.rb
class CreateUsers < ActiveRecord::Migration[6.0]
  def up
    create_table :users do |t|
      t.string :name
      t.timestamps
    end
  end

  def down
    drop_table :users
  end
end
💡

Sometimes you cannot afford to invest time in a down method. That is fine, raise an error and tell your team before merging. Once it is committed, editing a migration is hard and risky.

Roland Lopez
Written by
Roland Lopez

Technical founder & AI crack-head

Built by Agent Skynet See the agency