In the process of upgrading to Rails 6.1, I came across a new exception with poor coverage on Google:


DEPRECATION WARNING: Initialization autoloaded the constants CustomErrors, Defines::SourceControl.

Being able to do this is deprecated. Autoloading during initialization is going
to be an error condition in future versions of Rails.

Reloading does not reboot the application, and therefore code executed during
initialization does not run again. So, if you reload CustomErrors, for example,
the expected changes won't be reflected in that stale Module object.

These autoloaded constants have been unloaded.

In order to autoload safely at boot time, please wrap your code in a reloader
callback this way:

    Rails.application.reloader.to_prepare do
      # Autoload classes and modules needed at boot time here.
    end

That block runs when the application boots, and every time there is a reload.
For historical reasons, it may run twice, so it has to be idempotent.

Check the "Autoloading and Reloading Constants" guide to learn more about how
Rails autoloads and reloads


linkWhat it means

The Rails guide to Autoloading & Reloading Constant will give plenty of blah-blah-blah about Zeitwerk and why the autoloading system needed to be changed from the classic as of Rails 6. The important detail is this: any constant that is referenced in the course of invoking the files from config/initializers will be listed by the deprecation message unless it was ready to be referenced.


What does "ready to be referenced" mean? As you'll read below, it means that either the constant was explicitly required, or the code that calls the constant was wrapped in Rails.application.reloader.to_prepare do


linkHow to fix it

In spite of being long on words, the comment is short on useful follow up actions. After 30 minutes of Googling, this was the best process I could determine in order to resolve this:


link1. Determine where the constant is being referenced

The easiest way I found to determine this was following the instructions suggested here, and to add pp caller_locations to print the callers for the module that is being listed. For example, in the deprecation warning above, Rails mentions that CustomErrors was autoloaded, so I add pp caller_locations at the top of my custom_errors.rb file to print in console the full callstack by which the constant is being invoked. Or, if you just want to see the location from your initializers directory where the constant is being referenced, you can add this in front of the constant:


# lib/defines.rb
pp caller_locations.select { |l| l.to_s.index("config/init") }
module Defines
 ...
end


Once we have the callstack that is invoking the constant, then we can choose how to remedy it.


link2. require the constant or wrap it

In the case of CustomErrors, looking at the callstack indicated that it was being referenced by config/initializers/devise.rb. Thus, easiest way to fix the warning is by adding require "custom_errors" to the top of config/initializers/devise.rb


If for some reason you aren't able or don't want to require the module's file, the other option is to wrap the caller in the incantation provided in Rails' deprecation message. In the case of the devise initializer, it would look something like


Rails.application.reloader.to_prepare do
  Devise.setup do |config|
     ... a bunch of initilization, including calling to CustomErrors
  end
end


Which will reference the constant in question in a way that is reload-safe, thus removing the deprecation message.