May 16, 2020
Recently, I realized to write schema-only changes during migrations because it produces far less errors in the future. This means avoiding the use of models or service classes as part of your migration files.
When you use a model inside your migration, you’re tight-coupling your model to your database migration. I used to do this before:
class Migration < ActiveRecord::Migration
def change
add_column :users, :status, :string, default: "pending", null: false
User.reset_column_information
User.all.find_each do |user|
user.update(status: "completed") if user.enrolled?
end
end
end
Looking at the migration file alone, I see several reasons why this might fail in the future.
User#enrolled?
won’t exist 2 months from now?User
to Customer
because it makes more sense at the
time?If these are the cases, then your migrations won’t work anymore because
enrolled?
and your model User
are now tightly-coupled to your migration
process.
Instead of adding your post-migration logic to the migration file directly,
place it somewhere in your Rails app instead (e.g. db/scripts
or a rake task).
# db/scripts/add_user_status_migration.rb
User.all.find_each do |user|
user.update(status: "completed") if user.enrolled?
end
Then after deployment, run the script using rails runner
:
$project bin/rails runner db/scripts/add_user_status_migration.rb
And lastly, remove the script because you won’t need it anymore.
If you get used to this workflow, then your migrations should always work — keeping you away from unnecessary headaches.
Thanks!