skip to content

Adopting RuboCop on a five-year-old Ruby codebase, how do you decide which departments and cops the team enforces, and how do you encode that in .rubocop.yml?

level: principalimportance: should knowfreq 25%

answer

  1. correctness before taste
  2. Lint and Security fail the build
  3. Severity per department
  4. AllCops FailLevel since 1.91
  5. DisabledByDefault for an opt-in ruleset

basics

~20 s

Enforce what prevents bugs first: Lint and Security fail the build; Layout is autocorrected once; Style and Metrics are chosen deliberately. Encode it with department and cop Enabled, Severity, AllCops FailLevel and, for a strict opt-in list, DisabledByDefault.

solid answer

~40 s

There is no single right ruleset, so I would decide by **cost of a violation**. `Lint` and `Security` find likely bugs, so they gate the build. `Layout` is mechanical and autocorrectable, so it is enforced once the codebase has been formatted in one sweep. `Style` is where teams argue, so I keep defaults except where the team agrees otherwise, and write the reason next to each override. `Metrics` cops are signals about complexity, not correctness, so on old code they report without blocking. In `.rubocop.yml` that becomes department `Enabled` and `Severity` keys, `AllCops: FailLevel: warning` in RuboCop 1.91 (or `--fail-level warning` before it) so only warning and above fail CI, a `NewCops` policy, and `TargetRubyVersion`. A team that wants a short explicit list can set `AllCops: DisabledByDefault: true` and enable only what it names.

code

yaml · 20 lines
yaml
# .rubocop.yml - legacy app, correctness first
inherit_mode:
  merge:
    - Exclude

AllCops:
  TargetRubyVersion: 4.0
  NewCops: enable
  FailLevel: warning        # convention/refactor are reported only
  Exclude:
    - "db/schema.rb"

Metrics:
  Severity: refactor        # below the fail line

Style/Documentation:
  Enabled: false            # team decision: YARD on public API only

Naming/FileName:
  Severity: warning         # misnamed files break autoloading

go deeper

for a junior

Recall the departments and that Lint and Security findings are more serious than Style or Layout ones.

for a middle

Explain severities, the default fail line at refactor, and how Enabled and Severity at department or cop level change what fails.

for a senior

Design a rollout for a legacy app: blocking correctness cops, one Layout sweep, reported Metrics, and a strategy for existing offenses.

for a principal

Own the ruleset as a policy: justify each blocking department by the cost of its violations, document every override, and align it across services.

## Why this is a judgement call RuboCop's default configuration enables most of its several hundred cops. On a five-year-old application that typically means thousands of offenses on the first run, most of them about style rather than correctness. Turning everything on and blocking merges stalls delivery; turning everything off wastes the tool. The decision is which **classes** of offense are worth failing a build over, and the answer differs by department. ## A department-by-department policy | Department | What it catches | Suggested stance on legacy code | |---|---|---| | `Lint` | likely bugs, shadowed variables, unreachable code | enforce; fail the build | | `Security` | dynamic `eval`, `YAML.load`, `Kernel#open` misuse | enforce; fail the build | | `Layout` | whitespace and alignment | enforce after one autocorrect sweep | | `Naming` | method, variable and file names | enforce for new code; renames are risky | | `Style` | idiom choices | defaults, plus a few agreed overrides | | `Metrics` | method length, ABC size, complexity | report, do not fail; or disable | | `Bundler`, `Gemspec` | Gemfile and gemspec hygiene | enforce; cheap to satisfy | RuboCop's own direction supports this split. Version 1.91 added a **Preview** channel holding the defaults expected in 2.0, and under Preview it disables most `Metrics` cops and a list of contested `Style` cops (`Style/Documentation`, `Style/GuardClause`, `Style/IfUnlessModifier` and more), and sets `AllCops: FailLevel` to `warning`, so style offenses are reported without failing the build. ## Encoding the policy **Severity and the fail line.** Every offense carries a severity: `info`, `refactor`, `convention`, `warning`, `error`, `fatal`. `Lint` and `Security` default to `warning`, `Metrics` to `refactor`, everything else to `convention`. The run fails at `refactor` and above by default. Two knobs move the line: - `AllCops: FailLevel: warning` (new in 1.91) or `--fail-level warning` on the command line: convention and refactor offenses are printed, only warning and above fail; - `Severity:` on a department or cop, to promote a rule the team does treat as a blocker (`Naming/FileName: Severity: warning`, because a misnamed file breaks autoloading) or demote something to `info`, the lowest level, which fails a run only if the fail level is lowered to `info` as well. **Opt-out or opt-in.** The default model is opt-out: every enabled cop runs unless you disable it. `AllCops: DisabledByDefault: true` flips to opt-in: every cop except `Lint/Syntax` is off, and only cops or departments your configuration enables run. A department re-enabled that way brings back only its cops that are enabled by default. **Scope and version.** Set `TargetRubyVersion` so version-gated cops fire, and put generated files (`db/schema.rb`, `bin/*`) under `AllCops: Exclude` with `inherit_mode: merge` so the default exclusions survive. **New cops.** Choose a `NewCops` policy so upgrades are reviewed rather than ignored. ## Trade-offs to weigh 1. **Consistency versus churn.** Mass autocorrect of `Style` and `Layout` gives consistency but buries authorship; `git blame` points at the sweep commit unless git is told to skip it with its `blame.ignoreRevsFile` setting. 2. **Blocking versus reporting.** A failing `Metrics` cop on a legacy method punishes whoever touches it next; a reported one informs a refactoring backlog. 3. **Local freedom versus fleet uniformity.** One shared base config across services lowers review friction; per-repository overrides let teams respect local constraints. Every override should carry a comment saying why. 4. **Existing offenses.** Whatever the ruleset, the offenses already in the code need a strategy (a generated todo file, a ratchet, or a cleanup campaign) so the gate applies to new code from day one. ## Measuring before deciding Decisions go better with numbers than with taste. Before choosing, run the full default ruleset once with `rubocop --format offenses`, which prints a count per cop, sorted by frequency. The handful of cops that produce most of the offenses are where the team conversation belongs; a cop with three offenses is cheaper to fix than to debate. Re-run the same report after each rollout step to show the counts falling. ## A workable rollout - Week one: `Lint`, `Security`, `Bundler` and `Gemspec` blocking; everything else reported. - A single `Layout` autocorrect commit, then `Layout` blocking. - A short team review of the `Style` cops that fire most, ending in explicit `Enabled` or `EnforcedStyle` choices. - `Metrics` left at `refactor` below the fail line, or disabled with a written reason.

  • With AllCops DisabledByDefault true, what does adding `Style: Enabled: true` turn on?
    Only the `Style` cops that are enabled in RuboCop's default configuration. Cops that ship with `Enabled: false` stay off unless named individually. `Lint/Syntax` runs regardless, because it cannot be disabled. This lets a team build a short opt-in list by department and by cop.
  • How do you make Lint offenses fail CI while Style offenses only show up in the output, before RuboCop 1.91?
    Run `rubocop --fail-level warning`. `Lint` cops report at `warning` by default and most `Style` cops at `convention`, which is below that line, so they are printed but do not change the exit status. From 1.91 the same line can live in the file as `AllCops: FailLevel: warning`.

saying these in an interview costs you the question

  • Enable every cop at once and block merges until all offenses are fixed.
  • Severity changes which offenses a cop reports, not only their level.
  • Metrics offenses are bugs and should gate the build like Lint.
  • DisabledByDefault switches off Lint/Syntax as well.
  • FailLevel: warning hides convention offenses from the output.