skip to content

Which lint rules are safe to autofix, and what makes an automatic fix unsafe?

level: middleimportance: should knowfreq 52%

answer

  1. Not every rule can carry a fix
  2. Same behaviour, one obvious correction
  3. Run the fixer twice and compare
  4. A wrong finding now edits correct code
  5. A vanished warning is not a repair

basics

~20 s

A fix is safe when it preserves behaviour and the violation has exactly one obvious correction - whitespace, ordering, redundant syntax. It is unsafe when the repair depends on author intent the tool cannot know.

solid answer

~50 s

Rules divide into ones that carry a fix - a concrete source edit the tool can apply unattended - and **report-only** rules that just point at a position. A fix is safe under four conditions: it preserves behaviour, the correction is uniquely determined, applying it twice changes nothing the second time, and the finding was right in the first place. The last one is the one people forget: a report-only false positive costs a minute of reading; a false positive with a fix enabled edits correct code at scale. So formatting and redundant-syntax rules are good fix candidates, and most correctness rules are not - 'this resource is never released' has several valid repairs and the tool cannot know which one you meant. Before enabling a fixer, run the rule report-only to gauge its precision, apply it to a scratch copy, and run it twice to check it converges.

code

pseudocode · 12 lines
pseudocode
// before: `guard` is never read, but its scope owns the release
function bookSlot(patientId, slotId):
    guard = pool.acquireSlotLock(slotId)
    if hasConflict(patientId, slotId):
        return CONFLICT
    return persistBooking(patientId, slotId)

// after the mechanical "remove unused local" fix: still compiles, no lock held
function bookSlot(patientId, slotId):
    if hasConflict(patientId, slotId):
        return CONFLICT
    return persistBooking(patientId, slotId)

go deeper

for a junior

Know that some rules ship a fix the tool can apply for you and others only report a position. Be able to say why spacing is safe to fix automatically while 'this value is never used' may not be.

for a middle

Explain the mechanics: behaviour preservation, a uniquely determined correction, idempotence across repeated passes, and the fact that a fix inherits the rule's false-positive rate. A worked example of a fix that compiled and broke something is the strongest answer here.

for a senior

Demonstrate the rollout judgement - measure precision report-only first, apply to a scratch copy, verify convergence, keep the mass edit in its own change so the reviewable diff stays reviewable. Say plainly why you keep correctness rules report-only.

for a principal

Own the policy: which classes of rule may ever edit code unattended, who signs off on enabling a fixer, and how you stop a repository-wide rewrite from destroying the review signal and the change history that incident work depends on.

### What an automatic fix actually is Some rules ship with a *fix*: alongside the reported position, the rule emits a concrete edit — a range of the source and the text that should replace it. The tool can then apply the edit and re-report, so the violation disappears without a person touching the file. Rules without a fix are **report-only**: they tell you where to look and leave the repair to a human. Whether a rule can carry a fix is not a property of the tool's ambition; it is a property of the rule. ### The four conditions for a safe fix 1. **Behaviour preservation.** The edit must not change what the program does. Reordering imports, normalising indentation, deleting a redundant pair of parentheses — the token stream that matters is unchanged. This is a claim about the rule's implementation, not a law: a fix that reflows a line in a language whose parser treats line breaks as significant, or that rewrites the interior of a string literal, can absolutely change behaviour. 2. **A uniquely determined correction.** There must be exactly one right answer. "Indent this block four spaces" has one. "This resource is never released" has many: release it here, restructure the method, hand ownership to the caller. When the repair depends on intent, the tool cannot know it, and any fix it invents is a guess with a compile-clean disguise. 3. **Idempotence and convergence.** Applying the fix to already-fixed code must produce no further change, and a pass over the file must terminate. Rules whose fixes conflict — two rules editing overlapping ranges, or one rule's output re-triggering another — force the tool into repeated passes, and a non-converging pair will oscillate until the pass cap stops it. 4. **A finding that was correct in the first place.** This is the condition people forget. A report-only false positive costs a minute of reading. A false positive *with a fix enabled* is a tool that edits correct code, unasked, at scale. ### The unsafe fix, worked through In the appointment scheduler, a rule reports "local assigned but never read" on a booking path. The binding held a scope guard: the handle it named was released when the enclosing scope ended, and nothing else read the variable. The mechanical fix for an unread local is to delete the statement. ```pseudocode // before -- the guard is never read, but its lifetime owns the release function bookSlot(patientId, slotId): guard = pool.acquireSlotLock(slotId) // released when `guard` leaves scope if hasConflict(patientId, slotId): return CONFLICT return persistBooking(patientId, slotId) // after the "unused local" autofix -- the call is gone with the binding function bookSlot(patientId, slotId): if hasConflict(patientId, slotId): return CONFLICT return persistBooking(patientId, slotId) ``` The build stayed green, the test suite stayed green, and two clinicians could now be handed the same 09:15 slot. Change the fix slightly — keep the call but drop the binding — and the handle is acquired and never owned by anything, which is the other failure: after about 1,100 bookings the 64-handle pool is exhausted and the whole service stalls. Both edits are "correct" against the rule as stated. Neither is correct against the program. ### Fix versus suggestion Because safety is graded rather than binary, several rule engines split corrections into two tiers: edits considered safe enough to apply in bulk, and *suggestions* offered to a human one at a time, typically in an editor. The distinction is worth reaching for in an interview, because it names the real design pressure — an author with the code in front of them can accept a guess; a bulk run across a repository cannot. ### How to adopt a fix responsibly Run the rule report-only first and read a sample of its findings; that measures the rule's precision, which is the dominant term in fix safety. Then apply the fixer to a scratch copy of the repository and look at what changed: how many files, and what kinds of edit. Run it twice and diff the two results — a difference means the fix does not converge. Run the test suite over the fixed tree, remembering that a green suite is weak evidence for exactly the branches the tests do not cover. Finally, land formatting-scale fixes in their own change, separate from behavioural work; a fix run that rewrites 3,400 files and one real edit is a diff nobody reviews, and a diff nobody reviews is where the interesting mistake hides. ### The judgement to state out loud The reason to prefer report-only for a correctness rule is not timidity. It is that the rule detects a *symptom*, and applying a mechanical repair to a symptom converts a visible problem into an invisible one. A fix that makes the finding go away is not the same thing as a defect repaired — and a candidate who says that sentence has answered the question.

  • How would you validate a new fixer before turning it on across a whole repository?
    Run the rule report-only first and read a sample of findings, because precision dominates fix safety. Then apply the fixer to a scratch copy and inspect what changed - how many files, what kinds of edit. Run it a second time and diff the two results to confirm it converges. Run the test suite, remembering that green is weak evidence for the branches the tests never take. Then land it as its own change.
  • What is the difference between an automatically applied fix and a suggestion?
    Safety is graded rather than binary, so several rule engines expose two tiers: edits considered safe enough to apply in bulk, and suggestions offered to a person one at a time while they have the code in front of them. The distinction captures the real pressure - an author can evaluate a guess in context, and an unattended repository-wide run cannot.
  • Two rules both want to edit the same span of code. What does the tool do?
    It cannot apply both edits to overlapping ranges, so it typically applies one, re-analyses, and repeats in passes until nothing changes or a pass cap is hit. That is fine when the rules converge and pathological when one rule's output re-triggers another - the pair oscillates until the cap stops it, leaving the file in whichever state the last pass produced.

Autofix is autocorrect. It is excellent at spelling, where there is one right answer, and it is a menace the moment it starts guessing at what you meant to say.

saying these in an interview costs you the question

  • Enables fixes for every rule and never reads the diff
  • Assumes an automatic fix cannot change behaviour
  • Never checks that running the fixer twice converges
  • Treats a vanished warning as a defect repaired
  • Forgets that a false positive's fix damages correct code
  • Mixes a repository-wide fix run into a behavioural change

context