Prevent Rails from auto-loading app/ code when running database migrations
This gem prevents Rails from auto-loading app code while it’s running migrations,
preventing the common mistake of referencing ActiveRecord models from migration
code.
Add good_migrations
to your Gemfile:
gem 'good_migrations'
And you’re done! That’s it.
This gem requires that your app uses either of these autoloader strategies:
ActiveSupport::Dependencies
autoloader (e.g. config.autoloader = :classic
), which is going away with Rails 7config.autoloader = :zeitwerk
) If your app uses an earlier version of zeitwerk, you’ll see adb:migrate
is runOver the life of your Ruby on Rails application, your
app’s models will change dramatically, but according to the Rails
guides, your migrations shouldn’t:
In general, editing existing migrations is not a good idea. You will be
creating extra work for yourself and your co-workers and cause major headaches
if the existing version of the migration has already been run on production
machines. Instead, you should write a new migration that performs the changes you
require.
That means that if your migrations reference the ActiveRecord model objects
you’ve defined in app/models
, your old migrations are likely to break. That’s
not good.
By adding this gem to your project’s Gemfile
, autoloading paths inside 'app/'
while running any of the db:migrate
Rake tasks will raise an error, explaining
the dangers inherent.
Some will reply, “who cares if old migrations are broken? I can still run rake db:setup
because I have a db/schema.rb
file”. The problem with this approach
is that, so long as some migrations aren’t runnable, the db/schema.rb
can’t
be regenerated from scratch and its veracity can no longer be trusted. In
practice, we’ve seen numerous projects accumulate cruft in db/schema.rb
as the
result of erroneous commits to work-in-progress migrations, leading to the
development and test databases falling out of sync with production. That’s not
good!
For more background, see the last section of this blog post on healthy migration
habits
If you add good_migrations
to an existing application and any of those
migrations relied on auto-loading code from app/
, then you’ll see errors
raised whenever those migrations are run.
You have several options if this happens:
good_migrations
gem to ignore migrations prior to a specified date with theTo configure the gem, call GoodMigrations.config
at some point as Rails is
loading (a good idea would be an initializer like
config/initializers/good_migrations.rb
)
GoodMigrations.config do |config|
# Setting `permit_autoloading_before` will DISABLE good_migrations for
# any migrations before the given time. Don't set this unless you need to!
#
# Accepts parseable time strings as well as `Date` & `Time` objects
# config.permit_autoloading_before = "20140728132502"
end
The gem only prevents auto-loading, so you can always can explicitly require
the app code that you need in your migration.
If needed, it is possible to run a command with good_migrations
disabled by
running the command with the env var GOOD_MIGRATIONS=skip
.
Credit for figuring out where to hook into the ActiveSupport autoloader goes
to @tenderlove for this
gist. And thanks to
@fxn for implementing the hook necessary for zeitwerk
support to be possible.
Because this gem works by augmenting the auto-loader, it will not work if your
Rails environment (development, by default) is configured to eager load your
application’s classes (see:
config.eager_load).