Open-Source Wikis

/

Ruby on Rails

/

How to contribute

/

Patterns and conventions

rails/rails

Patterns and conventions

The conventions that span every Rails component. Drawn from AGENTS.md, .rubocop.yml, and the codebase itself.

Code style

File header

Every Ruby file starts with:

# frozen_string_literal: true

.rubocop.yml enforces it. New apps generated by rails new (8.2+) also enable frozen string literals via config/bootsnap.rb.

assert_not over assert !

Rails/AssertNot cop forbids assert !condition. Use assert_not condition or refute condition.

Markdown comparisons

In view tests, prefer assert_dom_equal over plain string equality. It normalizes attribute ordering and whitespace.

Sentence case in headings

In guides and YARD/RDoc comments. ("Getting started with authentication", not "Getting Started With Authentication".)

Whitespace and naming

  • 2-space indentation.
  • snake_case for files, methods, and locals.
  • CamelCase for constants and modules.
  • Files match constants: ActiveRecord::ConnectionAdapters::PostgreSQLAdapter lives in active_record/connection_adapters/postgresql_adapter.rb (Zeitwerk requires this).

Configuration flags

Configuration options follow a consistent pattern.

1. Define on the base class

class ActionView::Base
  cattr_accessor :remove_hidden_field_autocomplete, default: false
end

cattr_accessor comes from Active Support (active_support/core_ext/module/attribute_accessors.rb).

2. Read the flag at the call site

@options.reverse_merge!(autocomplete: "off") unless ActionView::Base.remove_hidden_field_autocomplete

Read the full constant path every time. Don't memoize it on the instance.

3. Wire into load_defaults

# railties/lib/rails/application/configuration.rb
case target_version.to_s
when "8.1"
  action_view.remove_hidden_field_autocomplete = true
end

load_defaults is how a Rails version opts an app into recommended behavior. Adding a flag without wiring it into load_defaults is fine for opt-in features but unusual for new defaults.

4. Document in the configuring guide

guides/source/configuring.md lists every framework configuration option. Add yours there.

Module composition

ActiveSupport::Concern

The standard mixin pattern. Use it whenever a module needs class methods, includes another concern, or runs hooks at inclusion time:

module Cacheable
  extend ActiveSupport::Concern

  included do
    after_save :touch_cache
  end

  class_methods do
    def cache_key_for(record)
      "#{name}/#{record.id}"
    end
  end
end

Concern (activesupport/lib/active_support/concern.rb) avoids the boilerplate of included(base) hooks and resolves dependency ordering automatically.

Lazy load hooks

Code in one component that needs to extend another (e.g., Active Storage adding methods to ActiveRecord::Base) uses ActiveSupport.on_load:

ActiveSupport.on_load(:active_record) do
  include ActiveStorage::Reflection::ActiveRecordExtensions
end

The hook fires once ActiveRecord::Base is fully loaded. Implementation in activesupport/lib/active_support/lazy_load_hooks.rb.

File organization

Per AGENTS.md:

  • lib/ — production code.
  • test/ — Minitest test files (always, never spec/).
  • bin/ — executables (e.g., bin/test).
  • Each component has its own Gemfile-like dependencies via gemspec.
  • Shared tooling lives in tools/.

Within Action View, helpers are split into three layers — modify all three for consistency:

  • actionview/lib/action_view/helpers/tags/<element>.rb — individual form elements (hidden_field, text_field, etc.).
  • actionview/lib/action_view/helpers/form_helper.rb — high-level form builders.
  • actionview/lib/action_view/helpers/form_tag_helper.rb — standalone tag helpers.

Within Active Record, related work usually splits into lib/active_record/<feature>.rb (top-level module) plus a lib/active_record/<feature>/ directory for sub-pieces.

Deprecation pattern

Each component owns a Deprecator:

# activerecord/lib/active_record/deprecator.rb
module ActiveRecord
  def self.deprecator
    @deprecator ||= ActiveSupport::Deprecation.new("8.2", "Rails")
  end
end

Use it like this:

ActiveRecord.deprecator.warn("`schema_order` is deprecated; use `schema_search_path`.")

Per-component deprecators let applications silence or escalate deprecation warnings on a per-gem basis. The shared base class is ActiveSupport::Deprecation in activesupport/lib/active_support/deprecation.rb.

Changelog entries

Add to the top of <component>/CHANGELOG.md:

- Brief description of the change. Mention any user-facing API change
  or migration step.

  _Your Name_

Existing entries set the tone — short, present-tense, focused on user-visible impact.

Rescue and error reporting

For framework code, prefer:

begin
  do_thing
rescue SomeError => e
  Rails.error.report(e, context: { foo: bar })
  raise
end

Rails.error resolves to ActiveSupport::ErrorReporter, which fans out to every registered subscriber (Sentry, Honeybadger, application-specific reporters, etc.). The implementation is in activesupport/lib/active_support/error_reporter.rb.

Notification names

Notification events are named <verb>.<component_namespace>:

  • sql.active_record
  • process_action.action_controller
  • render_template.action_view
  • enqueue.active_job
  • cache_read.active_support

When you publish a new event, follow this convention. The first segment is the action; the second is the component slug.

Testing patterns

See testing for the full list. The high-impact ones:

  • Use Object#with for temporary configuration changes.
  • Use freeze_time / travel_to from ActiveSupport::Testing::TimeHelpers.
  • Test both the default and the explicit-override case for any flag.
  • Group related tests in the same file rather than splitting per class.

Documentation

API docs use YARD/RDoc style:

# Returns the cache key for the given record.
#
#   user = User.find(1)
#   User.cache_key_for(user) # => "User/1"
def self.cache_key_for(record)
  ...
end

Guides under guides/source/ are Markdown. The configuring guide is the canonical place to document new configuration options.

For tooling that enforces these rules, see tooling.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Patterns and conventions – Ruby on Rails wiki | Factory