Separate the responsibilities
Put schema operations in the standard Rails methods and record changes in data hooks within the same migration.
Open-source Rails migration tool
A migration can change records as well as tables. But the models it calls keep evolving. Migration Data gives that record-changing code a dedicated hook you can test before it runs against production data.
MIT-licensed gem · Data hooks within Rails migrations
change or up method for tables and columns.dataKeep the backfill in a method your tests can call directly.rails db:migrate to apply the schema and execute the data hook.Tests check the behavior you specify. Review the migration against your application and production data before deploying.
Start with your existing workflow
Add migration_data to your application's Gemfile and install it. Include it in the environments that run your migrations.
Define a data method in a migration. The gem runs it after the usual schema operation when migrating up.
# Gemfile
gem 'migration_data'
# Terminal
bundle install
rails db:migrateRead installation and usage detailsRubyGems snapshot, October 13, 2026: 3,260,281 downloads. Counts include repeat downloads and do not represent unique users or active installations.
From the project community
Public feedback from migration_data users and contributors on GitHub. Each quote links to the original discussion.
The gem is also listed in Made in Belarus →, a collection of open-source projects made in Belarus.
User feedback · GitHub
I just used this gem with Rails 7.0.4 and minitest and it works fine, found it quite useful.
User feedback · GitHub
Hi, thanks for developing the awesome gem!
Contributor feedback · GitHub
Thanks @ka8725. Awesome work! Just arrived home and seeing this now
The problem behind the gem
A migration may work when you write it and fail later when a model is renamed, validation changes, or an association behaves differently. Testing the data logic makes those dependencies visible.
Put schema operations in the standard Rails methods and record changes in data hooks within the same migration.
Assert what changed in the records, not just that the method ran without raising an exception.
Run migration tests as the application evolves, so changes to models can expose broken data logic before deployment.
A practical example
This migration adds a string column and sets every existing user's status to pending. The data hook runs after change.
reset_column_information refreshes the model's column information before the update. This example uses a Rails 6.0 migration class; use the migration version appropriate for your application.
class AddStatusToUsers < ActiveRecord::Migration[6.0]
def change
add_column :users, :status, :string
end
def data
User.reset_column_information
User.update_all(status: 'pending')
end
endSmall hooks. Useful control
Give record updates their own data method. Exercise it directly in your test suite and check the resulting records, so model changes do not quietly break an old backfill.
Run the usual Rails migration task. The gem invokes the data hook after the schema change, keeping related schema and record changes together in one migration file.
Use data_before when records need changing before a schema operation. The data and data_after hooks run afterward. Choose the order that fits the change.
Define rollback_before, rollback, or rollback_after when a data change needs its own reversal. Write and test the reverse operation yourself; the gem does not infer it.
Require migration_data/testing and use require_migration to load a migration by name. Keep focused tests alongside the rest of your Rails test suite.
MigrationData.config.skip can disable data hooks in an environment where they should not run. Keep that choice explicit, especially when test setup already supplies the data.
Make the behavior explicit
Load the migration with require_migration, create a user with no status, and call the data method. Then assert that the user received the expected value.
This RSpec example assumes FactoryBot is configured and the test database already has the status column. It tests the record update separately from the schema operation.
Explore the testing helper →require 'rails_helper'
require 'migration_data/testing'
require_migration 'add_status_to_users'
RSpec.describe AddStatusToUsers do
describe '#data' do
it 'backfills existing users' do
user = create(:user, status: nil)
described_class.new.data
expect(user.reload.status).to eq('pending')
end
end
endKnow what the hooks do
On rollback, the schema operation runs before rollback and rollback_after. Use rollback_before if the reverse data operation needs a column that the schema rollback removes.
The gem organizes execution. You still decide how to reverse data changes and whether a large backfill needs batching or a separate deployment step.
Engineering knowledge, shared
Andrei's article explains the approaches that led to migration_data and why tests matter for migration code.
Read the original article →Actual DB Schema addresses another migration problem: database state left behind when you switch Git branches.
Explore Actual DB Schema →Read the source, report an issue, or propose an improvement to migration_data.
Visit GitHub →Built by WideFix founder Andrei Kaleshka
We maintain existing Rails applications, plan upgrades, and deliver production data changes. That hands-on work shapes the open-source tools we share with the community.
Explore our Rails maintenance services