Showing version 1 bot backfill · api · 2026-06-22 11:38:03

Sandi Metz' Rules


slug: sandi-metz-rules
title: "Sandi Metz' Rules"
category: engineering

Sandi Metz' Rules

When to use it: writing or reviewing Ruby/Rails classes, methods, controllers, or
views — or any time a class/method is growing large and you want to keep objects small,
focused, and replaceable. Source: Sandi Metz — Rules / POODR / 99 Bottles of OOP.

Guard-rails that keep objects small, focused, and replaceable. They are defaults, not
dogma
— see "Breaking a rule" below.

The four rules

  1. A class can be no longer than 100 lines of code.
  2. A method can be no longer than 5 lines of code.
  3. Pass no more than 4 parameters into a method. Hash options count as parameters — you
    can't sneak them in.
  4. Controllers can instantiate only one object. Therefore a view can only know about one
    instance variable, and it should send messages to only that object (@thing.parts, not
    @thing + @parts + @more).

A corollary that flows from all four: extract a class the moment a method or conditional
starts "knowing too much."

Why each rule exists (so you know when it's safe to bend)

  • 5-line methods force you to name intermediate concepts. A 5-line method that needs an
    if/else has its two branches extracted into methods, each ≤ 5 lines. This is how
    polymorphism replaces conditionals.
  • 100-line classes keep a single responsibility in view. A class crossing 100 lines is
    almost always two classes.
  • 4 parameters signals a missing object. Three+ related params → introduce a parameter
    object / value object.
  • One controller object kills the "instance-variable soup" that couples views to
    internals. Wrap the page in a presenter / facade object that exposes exactly what the view
    needs.

Breaking a rule

You should break these rules only if you have a good reason or your pair lets you.

In practice: a deviation is allowed when you can state the reason in the code review (or PR
body) in one sentence. "This serializer is 7 lines because splitting it hides the shape of
the JSON" is a reason. "I didn't feel like extracting" is not.

Applying in Rails

  • Fat model / fat controller smell → extract service objects (one public call),
    query objects (scoped relations), form/contract objects (validation),
    presenters (view-facing).
  • Replace conditionals with polymorphism: tier classes, state objects, or case on a
    small enum that delegates.
  • Value objects for money, scopes, identifiers — small immutable classes, not primitives
    passed around.
  • Controllers: each action loads or builds exactly one object and renders it.
    Authorization, fetching, and shaping belong in that object, not the action.

Review checklist

  • Any class > 100 LOC? → split by responsibility.
  • Any method > 5 lines? → extract named sub-methods.
  • Any method/initializer with > 4 params? → introduce a parameter/value object.
  • Any controller action with > 1 @ivar reaching the view? → wrap in a presenter.
  • Any if/elsif on a type/role/state? → consider polymorphism or a state machine.
  • Any rule broken without a one-line justification? → fix or justify.

See also