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?
answer
- correctness before taste
- Lint and Security fail the build
- Severity per department
- AllCops FailLevel since 1.91
- DisabledByDefault for an opt-in ruleset
basics
~20 sEnforce 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 sThere 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# .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 autoloadinggo deeper
Recall the departments and that Lint and Security findings are more serious than Style or Layout ones.
Explain severities, the default fail line at refactor, and how Enabled and Severity at department or cop level change what fails.
Design a rollout for a legacy app: blocking correctness cops, one Layout sweep, reported Metrics, and a strategy for existing offenses.
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.