Doing freelance with Rails, I keep facing an issue when I join an existing project:
outdated migrations that fail dozens of times during
To fix those, I need to do very hacky things, like commenting the failing lines, but also the already-migrated-schema ones which is very error-prone. Anyways, that's not the way it should be.
Should I use
? That's not what I read as a good practice.
After some thinking, I came up with the simple idea of separating schema migrations from data migrations.
Why?
Because it's always data migration that fail.
Eg.
class AddStatusToUser < ActiveRecord::Migration
def up
add_column :users, :status, :string
User.find_each do |user|
user.status = 'active'
user.save!
end
end
def down
remove_column :users, :status
end
end
Then you'd remove the `User` class and the migration is broken.
After some search, I found this very nice article that summarized all the pain-points coming from not separating data migrations and schema migrations : http://railsguides.net/change-data-in-migrations-like-a-boss
Here, Andrey Koleshko suggests the following syntax:
class
CreateUsers < ActiveRecord::Migration
def change
# Database schema changes as usual
end
def data
User.create!(name: 'Andrey')
end
end
I would suggest instead, something like creating two types of migrations : normal migrations, that remain unchanged, and data migrations, that would work the following way:
class CreateDefaultUser < ActiveRecord::DataMigration # Inherits from a different class: DataMigration
def up
User.create!(name: 'Andrey')
end
def down
User.find_by_name('Andrey').destroy
end
end
I reckon it makes sense to put it in different files, as this is not the same type of database modification.
And would be created calling
rails generate migration --data CreateDefaultUser
They would behave slightly differently:
- Ignored when running something like:
rake db:migrate --no-data # or
rake db:setup --migrations
- or at least non-blocking (rescuing exception as a warning)
The cool thing about this is that there is no backward compability issue here.
You could say that's ok, there's already a gem that does that, those who need it can use it. But that's not true, as most developers would find out about the gem way too late, when all their broken migrations are already there. Plus if we call it a "best practice", it should not be optionnal.
If it does make sense, I would definitely look at how to write such change.
Thanks for consideration.