Read these rules on the web at claudiob.github.io/style — the live version lays them out three folders deep, and every rule there carries an Approve and a Reject button. Vote on the ones you keep and the ones you would throw out; the tallies below are those votes, counted.
Thirty rules, ten to a part. A rule lives in the widest part it is true in.
🧑💻 Principles · 💎 Ruby · 🛤️ Rails
Vote counts read from the live counters on 24 August 2026. For the current standing, visit the page.
What holds in any language.
Don’t store your customers’ phone, email, last name, street in plain text.
79% approve — 11 approve, 3 reject
Never write a method nobody calls. Never rescue an error nobody has incurred.
90% approve — 9 approve, 1 reject
Two calls on the same object in one expression are the tell: the second is unneeded.
45% approve — 5 approve, 6 reject
Between two versions carrying the same meaning, always pick the shorter name.
73% approve — 8 approve, 3 reject
claimed_by, matching — methods named like a query must not have any side effect.
100% approve — 9 approve, 0 reject
Every method that can be private must be private.
100% approve — 11 approve, 0 reject
Add a single-line comment above each class/constant/method that is not private.
60% approve — 6 approve, 4 reject
Small commits with long descriptions (why the code was shipped) and linear history.
90% approve — 9 approve, 1 reject
Full coverage and not one line more. Never test code you don’t own.
70% approve — 7 approve, 3 reject
Don’t Titleize. It’s ZIP not Zip. It’s license not licence. Respect the apostrophes.
90% approve — 9 approve, 1 reject
What holds in Ruby.
At most 100 lines in a file. At most 100 characters in a line. Comments count.
44% approve — 4 approve, 5 reject
You don’t need Service.new.call(params). Any method call is a code smell.
71% approve — 5 approve, 2 reject
Don’t interpolate method names at runtime. Don’t send. Don’t break the rules.
43% approve — 3 approve, 4 reject
Ruby does not care what class an object belongs to. Embrace the freedom. 🦆
83% approve — 5 approve, 1 reject
def foo(bar) opening with baz = bar.baz should have been foo(bar.baz).
100% approve — 5 approve, 0 reject
Taking **attributes only to hand them to a method makes params hard to follow.
83% approve — 5 approve, 1 reject
No need for attr_reader :foo if @foo can be used instead.
62% approve — 5 approve, 3 reject
A method can only return in its first line of code before any work starts.
50% approve — 3 approve, 3 reject
Omit on a call’s arguments; keep the inner ones where parsing needs them.
67% approve — 4 approve, 2 reject
Enhance RuboCop for code that is easy to grep and clean to diff:
- Single quotes are the rule; double quotes only for interpolation and escape [1] [2]
- Trailing comma on the last line of multiline literals (closing brace on its line) [3] [4]
whenandelsesit one level in fromcaseandend[5]privatesits withclass; private methods are indented like public ones[6]
80% approve — 4 approve, 1 reject
What holds in Rails.
Extend with Concern, respect strong params, apply convention over configuration.
Embrace REST: endpoints are CRUD on resources. Add custom resources, never actions.
86% approve — 6 approve, 1 reject
Build off the main branch of rails/rails. Scrap any ~> from your Gemfile.
Order gems alphabetically and document inline why the app would break without them.
33% approve — 2 approve, 4 reject
Rails.cache wherever the same rows would be read or the same markup rendered.
Run tests with config.cache_store = :memory_store to verify caching works.
67% approve — 4 approve, 2 reject
Don’t pass instance variables to partials—use strict `` instead.
83% approve — 5 approve, 1 reject
Redirect out of the app with allow_other_host: true; after a non-GET with status: :see_other; break out of a frame with data: { turbo_frame: '_top' }.
50% approve — 3 approve, 3 reject
Eager-load associations. Check if records exist and loop over them in a single query.
Avoid the cost of User.all if all you display is User.select(:name, :email).
100% approve — 6 approve, 0 reject
Ensure every migration you write is reversible.
Use change before up/down. Use up_only before reversible { |dir| dir.up }.
100% approve — 6 approve, 0 reject
Clarify a decimal column price stores money with attribute :price, :amount.
All you need is to register a new type: Amount < ActiveRecord::Type::Decimal.
83% approve — 5 approve, 1 reject
Invoke encrypt on any sensitive data. Add deterministic: { fixed: false } to columns that must be queried or kept unique such as: User.find_by(email:).
83% approve — 5 approve, 1 reject
PostgreSQL offers native types and extensions that play well with Rails:
type: :citextto store case-insensitive strings such as emailsarray: trueto store an array in a single columncreate_enumto declare enums backed by names, not integers
83% approve — 5 approve, 1 reject
Cast your own vote at claudiob.github.io/style.