All Ruby on Rails templates

Modern Rails Rules

Rails 7+ best practices: conventions, generators, and the Omakase stack.

DevZone Tools2,240 copiesUpdated Apr 11, 2026Ruby on Rails
# CLAUDE.md — Modern Rails

## Stack

- Rails 7+ (Rails 8 if you can adopt it). Ruby 3.3+.
- The Omakase stack: **Hotwire** (Turbo + Stimulus), **Tailwind**, **importmap-rails** or **esbuild** for JS, **Solid Queue / Solid Cable / Solid Cache** for jobs/cache (Rails 8+).
- Skip the SPA. Server-rendered + Turbo handles 90% of "I need it to feel snappy" without React.

## Conventions

- "Convention over configuration" is the rule. When in doubt, do what `rails generate` does.
- One model per concept. Don't pile unrelated columns onto `User` because every other model joins to it.
- Skinny controllers, fat models — but don't let "fat models" become 1000-line god classes. Extract to **POROs** or service objects.

## Generators

- Use `bin/rails g model`, `g controller`, `g migration`. The defaults are good.
- Don't bypass generators to "save a step" — they keep test files, fixtures, and routes in sync.
- Customize generators in `config/generators` once if your defaults differ.

## Strong params

- Always permit explicitly:
  ```ruby
  params.require(:user).permit(:email, :name, :role)
  ```
- Never `permit!`. That's an open door.
- Define `*_params` private methods on every controller. One per resource action.

## Routes

- RESTful first. Add custom actions only when REST genuinely doesn't fit:
  ```ruby
  resources :invoices do
    member { post :send_email }
  end
  ```
- Nest at most one level deep. Beyond that, pull a separate controller.

## Concerns

- Use `app/models/concerns/` and `app/controllers/concerns/` for cross-cutting behavior with a clear name (`Sluggable`, `SoftDeletable`).
- Don't use concerns to "split a long file". Concerns are for reuse, not file organization.

## Service objects

- For multi-step operations, write a PORO with a single public method:
  ```ruby
  class Invoices::Send
    def initialize(invoice); @invoice = invoice; end
    def call; ...; end
  end
  ```
- Return a `Result.success(value)` / `Result.failure(error)` object. Don't raise from a service for known failure modes.

## Performance

- `bullet` gem in dev. Catches N+1s before they hit prod.
- `includes(:assoc)` for eager loading. `.preload` and `.eager_load` if you need to control the join strategy.
- `find_each` for iterating large tables. Default `each` loads everything.

## Don't

- Don't use callbacks for cross-resource side effects (`after_save` that touches a different table). Use service objects or jobs.
- Don't put business logic in views. Helpers exist; presenters / decorators exist.
- Don't store sensitive data in `Rails.cache` without `expires_in`. Cache stampedes are real.
- Don't disable Rails defaults (CSRF, ForceSSL, Strong Params) without understanding what you give up.

Other Ruby on Rails templates