What is the difference between a formatting lint rule and a correctness lint rule?
answer
- Rules split into two families
- One family is appearance only
- One family predicts a defect
- Behaviour-preserving versus evidence of a bug
- Attention belongs to the second family
basics
~20 sA formatting rule governs how code looks - spacing, indentation, ordering - and never changes behaviour. A correctness rule flags a shape that is likely a bug: a discarded return value, an unreachable branch, a resource never released.
solid answer
~50 sLinting is automated rule-checking over source code **without running it**, and the rules fall into two families. Formatting rules constrain appearance - indentation, line length, brace placement, import order. They are behaviour-preserving by construction, which is why a tool can apply them automatically and why arguing about them in review is waste. Correctness rules encode a likely defect: a resource opened on one path and released on none, a comparison that is always true, an ignored error result. A correctness finding means somebody has to think; a formatting finding means a tool should just fix it. Treating the two families identically is the usual mistake - the team either interrupts people over cosmetics until they stop reading the output, or buries a genuine defect finding in a stream of spacing complaints. Separate them, fix the cosmetic family silently, and spend the team's attention on the other one.
code
pseudocode · 7 linesfunction bookSlot(patientId, slotId):
handle = pool.acquire() // formatting rule: aligned assignment
if hasConflict(patientId, slotId):
return CONFLICT // correctness rule: handle never released here
result = persistBooking(handle, patientId, slotId)
pool.release(handle)
return resultgo deeper
Be ready to say in one sentence that a linter checks source code without executing it, and to give one concrete example from each family. Naming a discarded return value or an unreleased resource lands better than a vague 'it finds bugs'.
An interviewer expects you to explain why the split matters mechanically: the cosmetic family has a uniquely determined correct output, which is exactly what makes it safe to apply automatically, and the correctness family does not.
Show the operational consequence. Explain how mixing the families in one output stream trains people to skim, and describe how you would route each family differently so a genuine finding is never one line among thousands.
Own the cost model: every rule spends from one shared attention budget and must pay for it. Be ready to say which family you would enforce organisation-wide, which you would leave to each team, and how you would defend that split to people who want everything on.
### What a linter is, precisely A linter reads source code **without running it** and reports places where the code violates a rule. Each rule is a small, independent predicate over the program's structure: it walks the parsed syntax tree (and, where the analyser resolves symbols, the types those names refer to) and reports a position when a shape it recognises appears. The absence of execution is the defining property. A linter sees every branch in the file, including branches no test exercises and branches that only fire on a rare production path; in exchange, it understands far less than a running program does. Total path reach, partial understanding — that trade explains both families of rule and both of their failure modes. ### Family one: formatting rules A formatting rule constrains how the code *looks*: indentation width, brace and parenthesis placement, maximum line length, ordering of import declarations, trailing separators, blank-line policy around declarations, quote style. Two properties define the family. 1. **The correct output is mechanically derivable.** Given the violation, there is exactly one thing the code should look like instead, so the rule can carry a fix and apply it without asking anyone. 2. **Applying it does not change what the program does.** It is behaviour-preserving by construction, because the tokens are identical and only the trivia between them moves. The value of this family is not defect prevention — a wrongly indented block is not a bug. Its value is *diff hygiene* and the elimination of an entire genre of review argument. When formatting is decided once by a rule and applied by a tool, a change's diff contains only the change, blame stays meaningful, and no reviewer spends attention on brace placement. A team that argues formatting in review is paying salary for something a rule settles in an afternoon. ### Family two: correctness rules A correctness rule encodes a code shape that is *evidence of a defect*: a value returned and then discarded, a comparison whose operands make it constant, a branch that cannot be reached, a resource acquired on one path and released on none, a caught error swallowed without being recorded or rethrown. The rule does not prove a bug exists — it recognises a pattern that is usually one. That changes everything downstream: the finding needs a human decision, the repair is not mechanical (the tool does not know what the author meant to do), and the rule's worth is measured by how often it is right. A worked example. A hospital appointment scheduler holds a slot during booking by acquiring a connection from a shared pool. On the branch where the patient already has a conflicting appointment, the method returns early and never releases the connection. Every test passed: each one took the happy path, where the release does run. The symptom surfaced only in production, as a resource exhaustion during the Monday morning rush — after roughly 1,100 conflicting bookings the pool of 64 connections was empty and unrelated endpoints began timing out. A correctness rule that looks for "acquired here, no release on this path" flags exactly that early-return branch, statically, in a branch the tests never took. That is the whole argument for the family: it inspects branches rather than runs. ### The awkward middle band Between the two sits a third group that people argue about most: idiom and maintainability rules — naming conventions, a cap on function length or on the number of parameters, complexity thresholds, "prefer this construct over that one". They are not cosmetic, because they encode a judgement about how easy the code will be to change; they are not correctness, because a violation is not a defect. Handle them as opinions with an owner: adopt the ones the team actually agrees with, and keep them out of the family whose findings mean "stop and think". Be careful about claiming they reduce bugs — whether such style and complexity rules correlate with fewer defects is genuinely contested in the literature, and the honest interview answer is that the evidence is mixed rather than a confident number. ### Why the split drives every other decision - **Severity.** A cosmetic rule should be applied silently or turned off; there is little point in a human reading a warning that a machine could have fixed. A correctness rule earns a severity that actually interrupts someone. - **Automatic repair.** The mechanical-output property is exactly the property that makes a fix safe to apply unread. Correctness findings rarely have it. - **Attention.** Every finding, of either family, spends from one shared budget. Mixing thousands of spacing complaints into the same output as a resource-leak finding is how a real defect gets scrolled past. - **Review.** Formatting is a rule's job; whether the early return should release the connection or restructure the method is a person's job. ### Misconceptions worth naming A linter is not a compiler and not a type checker — proving that names and types line up is a separate discipline with different guarantees. It is not a substitute for tests: it reasons about shapes, not about whether the scheduler books the right slot. And a lint-clean codebase is not a correct codebase; it is a codebase with no instances of the specific shapes someone thought to write a rule for.
- Where do naming conventions and function-length caps fit - formatting or correctness?Neither cleanly. They are a third, middle band of idiom and maintainability rules: not cosmetic, because they encode a judgement about how easy the code is to change, but not correctness either, because a violation is not a defect. Most ruleset arguments live in this band. Treat them as opinions that need a team decision, and keep them out of the family whose findings are supposed to mean stop and think.
- If a formatting rule never catches a bug, why keep it in the ruleset at all?For diff hygiene and review focus. When formatting is decided once by a rule and applied by a tool, a change's diff contains only the change, blame history stays meaningful, and an entire genre of review argument disappears. The rule is cheap to satisfy and it settles the question permanently rather than per pull request.
- Can a formatting rule ever change behaviour?Yes, which is why behaviour preservation is a property of the rule's implementation rather than a guarantee of the family. Reflowing lines in a language whose parser treats line breaks as significant, rewriting the interior of a string literal, or moving a comment that a code-generation step reads can all change what the program does. Treat it as an invariant to verify, not to assume.
A formatting rule is the spell-checker on a manuscript; a correctness rule is the fact-checker. One can be applied without asking the author, the other cannot.
saying these in an interview costs you the question
- Says a linter runs the code to find bugs
- Claims every lint finding is a real defect
- Weighs an indentation complaint the same as a leak finding
- Thinks a linter replaces tests or type checking
- Argues brace placement in review instead of encoding it
- Assumes a lint-clean codebase is therefore correct