Why run the same static analysis rule in the editor, at pre-commit, and in the build rather than only in the build?
answer
- Same rule, three different moments
- Speed versus authority
- Local setup is never guaranteed
- Only one placement can block a merge
- One pinned version, one shared config
basics
~20 sEach placement buys something different: the editor gives feedback in seconds while you type, the pre-commit check keeps a whole commit clean, and the build is the only placement that runs for everyone and can actually block a merge.
solid answer
~50 sThe three placements trade feedback speed against authority. In-editor analysis is the fastest loop - the finding appears before the code is even saved - but it depends on each developer's local setup, so it can never be what guarantees the rule holds. A pre-commit check widens the scope to everything being committed and catches what the editor missed, at the cost of commit latency; it has to stay in the low seconds. The build is the slowest loop and the only authoritative one: it runs on a controlled machine, on the state that will merge, for every change. The invariant that makes the stack work is one shared, version-controlled configuration and one pinned analyser version, so all three report the same findings. Without that, the early layers teach people to distrust the late one, and you are back to a build-only loop with extra noise.
code
pseudocode · 15 linestask analyse(paths):
tool = project_tool("analyser", version = lockfile.version_of("analyser"))
config = repo_root() + "/analysis.config"
log("analyser", tool.version, "config", config, "files", count(paths))
return tool.run(config = config, paths = paths)
# editor integration
analyse([open_buffer_path])
# pre-commit check
analyse(staged_files())
# build step - the only caller whose exit code blocks a merge
report = analyse(changed_files(base = merge_base()))
fail_build_if(report.has_errors())go deeper
Be ready to name the three placements and say in one sentence what each is for: editor for speed, commit-time for catching the rest of the change, build for enforcement. Knowing that only the build runs for everybody is the point of the question.
Explain the mechanics: what each layer analyses (buffer, changed set, merged state), why the commit-time check must stay in seconds, and how one pinned version plus one committed configuration keeps the three layers from reporting different things.
Show the judgment about which checks belong where and why an ambush - a build blocking on something no earlier layer could show the author - destroys trust in the whole stack. Talk about feedback cost in real pipeline minutes, not in slogans.
Own the tradeoff between developer setup burden and enforcement guarantee, and be honest that the widely quoted cost-of-late-defect multipliers are contested even though the direction is not. Decide what the organisation standardises and what it leaves to teams.
### Three placements, one rule A static analysis rule is a fixed piece of logic: it reads source, decides whether a pattern is present, and reports a finding. Nothing about the rule changes when you move it. What changes is **when the author learns about it** and **whether anyone is obliged to act**. That is why mature teams run the same rule in three places rather than picking one. **In the editor.** The analyser runs continuously against the buffer, and the finding appears as you type or on save, usually within a second. The author still has the whole problem in their head, the fix costs a keystroke, and nothing has been committed, reviewed or built. The catch is that this placement is *per-developer configuration*. A new joiner without the plugin installed, or with it pointed at a different version, gets no signal at all — so the editor can never be the thing that guarantees the rule holds. **At pre-commit.** A hook runs the analyser over the content about to be committed. This widens the scope from "the file I have open" to "everything in this commit", which catches the file you edited three hours ago in a different window, and it runs on content rather than on unsaved buffers. It costs commit latency, so it must stay in the low seconds — scope it to the changed set and keep whole-repository work out of it. Local hooks are also skippable and are installed per clone, so like the editor, this layer is an accelerator, not a guarantee. **In the build.** The pipeline runs the analyser on a controlled machine, on the state that is actually going to merge, for every change, without depending on anybody's laptop. This is the only placement that can be *required*, and it is the one a merge condition should point at. It is also the slowest loop, which is exactly the problem the other two layers exist to solve. ### Why the slow loop is expensive Consider an insurance quote engine whose pipeline takes 27 minutes end to end. If the only place a rule fires is the build, then a single-character finding costs the author a 27-minute wait, a context switch into whatever they started next, a return trip to reload the problem, and another 27 minutes to confirm. Three such findings arriving one at a time — because the analyser stops at the first failing step, or because the author fixes them one per push — turn a two-minute correction into most of an afternoon. The same finding in the editor costs seconds. The general claim behind this is often stated as a fixed multiplier — "a defect found late costs N times more" — and you should be careful with it: the specific multipliers quoted in the literature are contested and depend heavily on how the study measured cost. The direction is not contested. Feedback that arrives while the author still has the context is cheaper than feedback that arrives after they have moved on. ### The invariant that makes the stack work Three placements only help if they agree. The moment the editor is quiet about something the build rejects — or worse, complains about something the build does not care about — developers learn to ignore the early layers, and you are back to a build-only loop with extra noise. Two things keep them aligned: - **One config, in version control, resolved from the repository root.** Editor, hook and pipeline read the same file. A rule enabled for the team is enabled everywhere; nobody has a private ruleset. - **One pinned analyser version.** The version is declared in the project, and the editor integration is configured to use the project-local analyser rather than whatever the plugin bundles. Analysers add and change rules between releases; two versions are two different rules engines. A useful practical test: the pipeline should invoke exactly the same entry point a developer can invoke by hand. If reproducing a build finding locally requires reading the pipeline definition and reconstructing a command, the layers will drift. ### Choosing what runs where The three placements do not have to run the *same set*. A sensible division: - Editor: fast, local, per-file rules plus format-on-save autofix. - Pre-commit: the same fast rules over the changed set, so nothing lands unformatted; keep it in seconds. - Build: everything, including analyses too slow or too whole-program to run interactively, plus the authoritative pass/fail. The rule that must not be broken is directionality: anything the build blocks on should already have been visible earlier. A gate that fires on something no earlier layer could have shown the author is an ambush, and it is the fastest way to make a quality programme unpopular.
- If the build enforces the rule anyway, what does the editor placement actually save?The cost of a late finding. In a 27-minute pipeline, a one-character violation costs a wait, a context switch out, a context switch back and another full run to confirm - and findings often arrive one at a time, so three of them can eat an afternoon. In the editor the same fix costs seconds while the author still holds the context.
- How fast does a pre-commit check have to be before people start skipping it?Low single-digit seconds. Past that it is felt on every commit and people either batch their commits or skip the check, and a check people skip is worse than none because it creates false confidence. Keep it scoped to the changed set, keep whole-repository and type-aware analyses in the build, and move anything slow out rather than accepting the latency.
- Should the editor be allowed to auto-fix findings on save?For deterministic, meaning-preserving fixes such as formatting, yes - it removes a whole class of findings before they exist. Be more careful with fixes that change behaviour or restructure code, since the author may not read them. Either way the fixer must be the same pinned version as the checker, or save-time fixes will produce code the build then rejects.
It is the difference between spellcheck underlining a word as you type, a proofread before you hand the page over, and the printer refusing the job. Only the last one is binding, and you would hate to discover the typo there.
saying these in an interview costs you the question
- Says editor checks make the build gate unnecessary
- Treats a local pre-commit check as the enforcement boundary
- Runs the whole-repository analysis inside the commit check
- Lets each developer's editor use its own bundled ruleset
- Assumes a clean editor guarantees a green build
- Treats build-only feedback as costing the same as editor feedback