リローディング無効化を許可するオプトイン設定 `dangerously_allow_disabling_reloading` を追加
Springに Spring.dangerously_allow_disabling_reloading オプションが追加され、通常は拒否される config.enable_reloading = false 状態での起動をオプトインで許可できるようになりました。デフォルトの安全側の挙動は変わらず、特定の条件を満たすアプリケーションのみを対象とした高度な設定です。
背景
Springはこれまで、config.enable_reloading = false(Rails 7.0以前では config.cache_classes = true)の状態で起動しようとすると、:ensure_reloading_is_enabled イニシャライザによって強制的にエラーを出して起動を拒否していました。この制約は、Railsアプリケーションがルートの再描画・I18nのリロード・to_prepare コールバックなどをSpringのリクエスト間で Rails.application.reloader.reload! に依存していることを前提としており、ほぼすべてのRailsアプリに適切なデフォルトです。
しかし、フォークされた子プロセス内でルートやI18nを含むすべてのリロード可能リソースをレイジーロードするよう構成されたアプリでは、この制約が不要な制限となります。そのようなアプリでは config.enable_reloading = false のまま動作させることで、テスト実行ごとにリローダーが数万ファイルを stat するコストを回避でき、大規模アプリケーションほど恩恵が大きくなります。
技術的な変更
lib/spring/configuration.rb に dangerously_allow_disabling_reloading アクセサが追加され、デフォルト値 false で初期化されます。
@dangerously_allow_disabling_reloading = false
class << self
# ...
# Opt-in: skip the `:ensure_reloading_is_enabled` initializer so Spring
# boots with `config.enable_reloading = false`. Default `false`. See
# README "Running Spring with reloading disabled" for what this gives up.
attr_accessor :dangerously_allow_disabling_reloading
end
lib/spring/application.rb の :ensure_reloading_is_enabled イニシャライザには、フラグが有効な場合に検査全体をスキップする next が追加されています。また、エラーメッセージにもこのオプションへの誘導文が追加されました。
変更前:
Rails::Application.initializer :ensure_reloading_is_enabled, group: :all do
if Rails.application.config.cache_classes
# ...
raise <<-MSG.strip_heredoc
Spring reloads, and therefore needs the application to have reloading enabled.
Please, set config.#{config_name} to #{set_to} in config/environments/#{Rails.env}.rb.
MSG
end
end
変更後:
Rails::Application.initializer :ensure_reloading_is_enabled, group: :all do
next if Spring.dangerously_allow_disabling_reloading
if Rails.application.config.cache_classes
# ...
raise <<-MSG.strip_heredoc
Spring reloads, and therefore needs the application to have reloading enabled.
Please, set config.#{config_name} to #{set_to} in config/environments/#{Rails.env}.rb.
(If you understand the trade-offs and want to disable Rails' reloader anyway,
set `Spring.dangerously_allow_disabling_reloading = true` in config/spring.rb.)
MSG
end
end
オプトインする場合は config/spring.rb に以下の1行を追加するだけです。
Spring.dangerously_allow_disabling_reloading = true
アクセプタンステスト(test/support/acceptance_test.rb)も追加されており、開発環境の設定ファイルに config.cache_classes = true を書き込んだ状態でも bin/rails runner が成功することを検証しています。
設計判断
設定名に dangerously_ プレフィックス** を付けることで、リスクを名前そのものに埋め込む設計が採用されました。
READMEのセクション見出しも「Running Spring with reloading disabled (advanced, risky)」となっており、名前・ドキュメント・コメントの複数箇所で同じ警告を繰り返す構成です。また、既存のエラーメッセージにもこのオプションへの誘導を加えることで、誤った環境で詰まったユーザーが自然にたどり着けるようになっています。デフォルト値を false に固定し、既存の拒否ロジックをそのまま維持した上でオプトイン経路だけを追加することで、後方互換性を完全に保っています。
READMEでは、このオプションを有効にした場合にSpringサーバー起動時点でリロード可能リソースが読み込まれていないことを確認するためのアサーション例も示されており、正しい使い方を積極的にガイドする設計になっています。
Spring.after_environment_load do
raise "routes were drawn at boot" if Rails.application.routes_reloader.loaded
raise "i18n locales where loaded at boot" unless I18n.backend.instance_variable_get(:@translations).nil?
end
まとめ
この変更は、「安全なデフォルトを崩さずに高度なユースケースへの抜け道を用意する」という設計原則の実践例です。dangerously_ という名前の選択・デフォルト false・エラーメッセージからの誘導・README上の使用条件の明示という複数の層で意図しない使用を抑制しながら、大規模アプリのテスト高速化という具体的な問題を解決しています。