rails/rails
Debugging
How to find what's going on when something in the Rails source goes wrong.
Logs
Every component has a log subscriber and a structured event subscriber:
*/lib/<component>/log_subscriber.rb*/lib/<component>/structured_event_subscriber.rb
These translate ActiveSupport::Notifications events into log lines. When you're debugging behavior in an application, raising the log level (config.log_level = :debug) and watching the appropriate subscriber's output is usually the fastest signal.
In a dev shell, ActiveSupport::Notifications.subscribe is a quick way to attach an ad-hoc listener:
ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
ev = ActiveSupport::Notifications::Event.new(*args)
puts "#{ev.duration.round(1)}ms - #{ev.payload[:sql]}"
endWhen the framework boot fails
Boot order is documented in railties/lib/rails/application.rb (the long comment at the top of the file). The phases are:
before_configurationcallbacks- Load
config/environments/<env>.rb before_initializecallbacks- Run all railtie initializers (in dependency order)
- Run application initializers
- Build middleware stack
to_preparecallbacksbefore_eager_load+eager_load!after_initializecallbacks
A common failure is an initializer that runs before its dependency is available. The before and after options on initializer ... declarations control ordering — see railties/lib/rails/initializable.rb.
To see the order initializers will run, in a Rails app:
bin/rails initializers(Implemented in railties/lib/rails/commands/initializers/initializers_command.rb.)
Reproducing a bug from an issue
The repo ships executable bug-report templates under guides/bug_report_templates/. The most useful are:
active_record_main.rb— boots SQLite + Active Record + a tiny Minitest example. Edit, then run withruby active_record_main.rb.action_controller_main.rb— boots Action Pack + Action Controller againstRack::Test.generic_main.rb— for issues that don't need a specific framework.executable_template.rb— generates a complete on-disk Rails app viarails newand runs commands against it.
These are how bug reports are expected to come in, and they're the fastest way to bisect a failure to a specific commit.
Bisecting
Because the repo has tags going back to 1.0, git bisect works well:
git bisect start
git bisect bad main
git bisect good v8.0.0
git bisect run bundle exec ruby guides/bug_report_templates/active_record_main.rbCombine with the bug report template above to get an automated bisect.
Common pitfalls
Test isolation
Some tests modify global state (autoload paths, Rails.application, time). They live in dedicated isolation directories and are run via rake test:isolated. If a test passes alone but fails in a suite, it's often a missing isolation marker.
Reloader and code reloading
Changes in development reload via ActionDispatch::Reloader (actionpack/lib/action_dispatch/middleware/reloader.rb) which delegates to ActiveSupport::Reloader. If you're seeing stale class references in a dev session, check that:
- The file is in an autoload path (
config.autoload_paths). - The constant name matches the file path (Zeitwerk is strict).
- You're not holding a reference across a request.
Connection pools
Active Record connections are per-thread. Forking workers (Puma, Unicorn) need to discard parent connections — ActiveSupport.on_load(:active_record) { ActiveRecord::Base.connection_handler.clear_active_connections! } style hooks live in activerecord/lib/active_record/railtie.rb.
Autoload vs eager_load
In dev, only the constants you reference are loaded. In production with config.eager_load = true, everything is loaded at boot. A bug that only shows up in production may be an eager-load failure — try bin/rails zeitwerk:check (in an app) or set config.eager_load = true locally.
Debugging tools
debuggem — added to the defaultGemfilefor new apps. The framework itself usesbinding.irborbyebugcalls in tests rarely; preferputsdebugging or attachingActiveSupport::Notificationssubscribers.Rails.error.report(exception)—ActiveSupport::ErrorReporteris the framework-blessed place to send exceptions you've handled but want to record.backtrace_cleaner—Rails.backtrace_cleanerstrips noisy framework frames from backtraces. To see the full thing, callcallerdirectly or temporarily disable the cleaner (Rails::BacktraceCleaneris inrailties/lib/rails/backtrace_cleaner.rb).
When tests pass locally but fail on CI
- Adapter difference. Active Record CI runs against all four adapters; check the failing adapter explicitly:
cd activerecord && bundle exec rake test:postgresql - Time zone or locale. CI runs in UTC. Wrap time-sensitive assertions in
freeze_timeortravel_to. - Parallel races. If a test depends on a global, parallelization may interleave it with a sibling. Mark it as not parallelizable.
- Frozen string literals. New code must include
# frozen_string_literal: true. RuboCop will complain locally if it doesn't.
For test infrastructure details see testing.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.