Why can an editor report a file clean while the build fails the same analysis rule on it?
answer
- Two runs, not two moods
- Analysis is deterministic; inputs are not
- Which version is actually running?
- Configuration is resolved, not assumed
- Print version and resolved config path
basics
~20 sBecause the two runs are not the same run. Different analyser versions, a different resolved configuration file, a different file set or content, or missing type and dependency information will each produce different findings. Static analysis itself is deterministic.
solid answer
~50 sStart from the fact that analysis is deterministic: same version, same bytes, same effective configuration gives the same findings. So one of those inputs differs. The common causes are an editor integration using its own bundled analyser instead of the project-pinned one; a configuration resolved from a different directory, or a user-level configuration merged on top; different content - unsaved buffer versus committed state, ignore rules honoured by one side only, generated sources that exist only after a build step; and rules that need type or dependency information degrading quietly when that context is missing. Diagnose it by making both runs print the analyser version, the absolute path of the resolved configuration and the file count, then compare three lines. Fix it structurally: one committed configuration at the repository root, one pinned version resolved from the project, and one entry point that editor, commit check and build all call.
code
pseudocode · 11 linesfunction run_analysis(paths):
tool = project_tool("analyser") # NOT the editor's bundled copy
config = resolve_config(from = repo_root())
effective = tool.dump_effective_config(config)
log("version =", tool.version)
log("config path =", absolute(config))
log("config hash =", hash(effective))
log("files in scope =", count(paths))
return tool.run(config = config, paths = paths)go deeper
Be ready to say that the same analyser, over the same code, with the same settings gives the same answer - so a disagreement means one of those three differs. Naming version and configuration as the first two things to check is enough at this level.
Explain the mechanics you would check in order: version resolution, upward configuration search and merged user-level settings, buffer versus committed content, ignore rules and generated sources, and analyses that need type information. Then show how you would print those inputs rather than guess.
Demonstrate a diagnosis that takes minutes: reproduce the exact command locally against committed content, compare effective-configuration hashes, and separate content differences from environment differences. Then argue for removing the class of problem instead of the instance.
Own the standard that prevents recurrence - one entry point, one pinned version, configuration in version control, inputs logged on every run - and be clear that suppressing rules to unblock builds is a governance failure that will quietly hollow out the gate.
### "It's clean here" is a claim about a specific run When an editor reports a file clean and the pipeline fails the same rule on the same file, the instinct is to call it flaky. It almost never is. Static analysis is deterministic: the same analyser version, over the same bytes, with the same effective configuration, produces the same findings. So the disagreement is evidence that one of those three inputs differs, and the diagnosis is a matter of finding out which. ### The usual causes, roughly in order of frequency **1. Different analyser versions.** Editor integrations often ship with a bundled analyser and use it unless told otherwise, while the pipeline uses the version pinned in the project. Rules are added, renamed, split and have their defaults changed between releases, so a newer engine in the pipeline reports things an older bundled engine never had. Fix: pin the version in the project manifest and configure the editor to use the project-local analyser. **2. A different config file was resolved.** Most analysers search upward from the file being analysed for the nearest configuration, and many merge a user-level or editor-level config on top of the project one. Open a file inside a nested module and the editor may resolve that module's config; the pipeline, invoked from the repository root, resolves the root's. Per-path overrides make this worse, because both runs then apply a real config — just not the same one. **3. Different file sets or different content.** The editor analyses the buffer, including unsaved edits; the hook analyses staged content; the pipeline analyses committed content on the merged result. Three different byte streams. Add ignore files honoured by one layer and not another, generated sources that only exist after a build step in the pipeline, and case-sensitivity or line-ending normalisation differences between a developer machine and a build machine, and you have several ways for "the same file" to differ. **4. Missing analysis inputs.** Rules that need type information, resolved dependencies or a compiled model degrade quietly when that context is unavailable — often to *fewer* findings rather than an error. An editor with a fully indexed project can therefore see a violation the pipeline misses, or the reverse, when the pipeline resolves dependencies the editor never fetched. This class is the one that produces the most confusing divergences, because both runs look successful. **5. Environment-dependent rule behaviour.** A ruleset keyed off an environment variable, a locale, a default encoding or an operating-system path convention will behave differently on a build machine than on a laptop. In a quote engine we once chased a rule about currency rounding that fired only in the pipeline: the rule was enabled by a profile the build activated and nobody's editor did. **6. Stale caches.** Analysers cache results keyed on file content and configuration. A cache key that does not include the configuration or the analyser version will happily serve findings computed under the old rules. ### How to diagnose it in minutes rather than hours Make both runs *say what they did*. Have the entry point print the analyser version, the absolute path of the resolved configuration and the number of files in scope, and print it in the build log as well as in the editor's output. Comparing three lines usually ends the investigation. If the printed values match, hash the effective configuration — many analysers can dump the fully merged, resolved ruleset — and compare the hashes; that catches a user-level override merging on top. Then reproduce deliberately: run the pipeline's exact command locally on the committed content, not on your working tree. If it fails locally, the difference was content or file set; if it passes locally, the difference was the environment or a build-produced input. ### The structural fix Diagnosis is cheap once, expensive every week. The durable fix is to remove the opportunity for drift: - One configuration file, committed, at the repository root, with per-path overrides expressed inside it rather than by scattering config files. - One pinned analyser version, resolved from the project, used by editor, hook and pipeline alike. - One entry point — a single task or script — that all three call, so the pipeline definition contains no analysis flags of its own. - Version-print in the log so drift is visible the first time it happens. Two responses mark a weak candidate here: calling it flaky, and "fixing" it by disabling the rule in the pipeline so the build goes green. The second converts a tooling inconsistency into a permanent hole in the gate.
- How would you prove which configuration the analyser actually loaded?Do not infer it - print it. Log the absolute path of the resolved configuration and a hash of the fully merged, effective ruleset from both the editor run and the build run. Matching paths with differing hashes means something merged on top, typically a user-level or editor-level configuration. That single comparison usually ends the investigation in a minute.
- Why do rules that need type or dependency information diverge more often than plain syntactic rules?They need a resolved dependency graph or a compiled model, and when that is unavailable most analysers degrade quietly rather than failing - producing fewer or different findings instead of an error. An editor with a fully indexed project and a build that has not resolved the same dependencies are then running materially different analyses, and both runs look successful.
- The build is red, the editor is clean, and the release is due. Why is disabling the rule in the build the wrong move?It converts a tooling inconsistency into a permanent hole in the gate, and it is almost never revisited. Reproduce the build's exact command locally against the committed content first; that tells you within minutes whether the difference is content, file set or environment. If you truly must unblock, suppress the single finding with a justification and an owner, not the rule.
saying these in an interview costs you the question
- Calls deterministic analysis flaky
- Assumes the editor plugin uses the project's pinned analyser
- Disables the rule in the build to go green
- Forgets the check ran on staged or committed content, not the buffer
- Never prints the analyser version in the build log
- Ignores caches keyed without the configuration or version