Doing freelance with Rails, I keep facing an issue when I join an existing project: outdated migrations that fail dozens of times during rake db:migrate
``
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
rake schema:load
``
? 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 : Change data in migrations like a boss – WideFix 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.