Static Code Analysis Explained: What It Catches and Misses (2026)
What static code analysis catches, what it cannot, why interfile taint plus an open source licence does not exist, and where AI review fits. Verified 2026-10-18.
Published:
Last Updated:
Direct answer
Static code analysis reads source code without running it, parses it into an abstract syntax tree, and applies rules and data-flow analysis to find bugs, vulnerabilities and maintainability problems. It is deterministic: the same code and ruleset return the same findings every run. That property, not detection depth, is why it remains the layer auditors accept and why it sits underneath AI review rather than being replaced by it.
What is static code analysis?
Direct answer: Static code analysis examines source code without executing it. The analyser parses code into an abstract syntax tree, then applies pattern rules, data-flow analysis and taint tracking to find defects. Because nothing runs, it needs no test data and no environment, and it covers code paths your tests never reach. Verified 2026-10-18.
The defining word is static. The program is never executed. The analyser reads the text, builds a model of the program’s structure, and reasons about what the code could do rather than observing what it does do. That is the opposite of dynamic analysis, which runs the application and watches it, and it is the source of both the strengths and the limits of the technique.
Three consequences follow from never running the code, and they explain most of what people find surprising about these tools.
It sees every path, including the ones your tests miss. A test suite only exercises the inputs you thought of. A static analyser walks the control-flow graph, so an error branch that has never executed in production is as visible to it as the happy path. This is the single largest coverage advantage the technique has.
It cannot see anything that only exists at runtime. Configuration loaded from the environment, a misconfigured load balancer, a permission check that passes in staging and fails in production, an authentication flow that depends on a third-party identity provider: none of these appear in the source text. That is what DAST exists for, and why running one without the other leaves a class of bug uncovered.
It approximates, and it chooses which way to be wrong. Rice’s theorem says non-trivial semantic properties of programs are undecidable in the general case. Every practical analyser therefore over- or under-approximates. Over-approximate and you get false positives. Under-approximate and you get false negatives. Commercial vendors overwhelmingly tune toward fewer false positives, because teams abandon a noisy tool far faster than they abandon a quiet one.
| Technique | What the analyser models | What it catches | Typical runtime cost |
|---|---|---|---|
| Lexical / regex scanning | Raw text | Hardcoded secrets, banned strings, TODO patterns | Milliseconds |
| AST pattern matching | Syntax tree of one file | Unused variables, if (x = 1) typos, deprecated API calls, insecure function use | Milliseconds to seconds |
| Intraprocedural data flow | Value movement inside one function | Null dereferences, unreachable code, resource leaks in a single scope | Seconds |
| Interprocedural data flow | Values across function boundaries in one file | Null passed through a helper, state mutated by a callee | Seconds to minutes |
| Interfile taint analysis | Untrusted input from source to sink across files | SQL injection, XSS, path traversal, SSRF in real layered code | Minutes on a large repo |
| Symbolic execution / abstract interpretation | Formal semantics of program states | Buffer overruns, integer overflow, division by zero, MISRA and CERT violations | Minutes to hours |
The bottom half of that table is where the money is. Anyone can ship regex scanning. Interfile taint analysis across a real framework-heavy codebase is the expensive engineering, and the pricing in this market reflects it almost exactly - see our buyer’s guide to static analysis tools for how that maps onto licences.
Static code analysis explained for developers
Direct answer: An analyser parses each file into an abstract syntax tree, matches rules against that tree, then layers data-flow analysis to track how values move. Taint tracking follows untrusted input from a source to a dangerous sink. Cross-file taint is the hard part, and in 2026 it is proprietary-only. Verified 2026-10-18.
Here is the pipeline, in the order the analyser actually executes it.
1. Parse. The source becomes an abstract syntax tree. An if statement becomes a node with a condition child and two branch children. This step is why analysers are language-specific: each language needs a grammar, and each grammar version needs maintenance. It is also why a tool’s language list is the single most load-bearing line on its marketing page.
2. Match. Rules run against the tree. A rule is a shape, not a string. eval($USER_INPUT) as a Semgrep pattern matches eval(req.body.cmd) and eval( req . body . cmd ) identically, because both produce the same tree. This is the entire reason AST matching beats grep, and it is what a linter does.
3. Resolve types and build a call graph. Compiler-based analysers get this for free because they run after the compiler. Source-only analysers approximate it. This is the first big fork in the market: Semgrep parses source and needs no build, while Coverity, Klocwork, clang-tidy and SpotBugs all need a successful build or compiled artefact. Build-dependent tools see more and break more.
4. Data-flow analysis. The analyser tracks how values propagate. Assigned here, passed there, returned from this function, stored in that field. Within one function this is cheap. Across functions it needs the call graph. Across files it needs the whole-program call graph, which is where the cost explodes.
5. Taint tracking. A specialisation of data flow for security. The analyser defines sources (untrusted input: request parameters, file reads, environment variables, message queues), sinks (dangerous operations: SQL execution, shell invocation, file path construction, HTML rendering) and sanitisers (functions that make tainted data safe). A finding is a path from a source to a sink with no sanitiser in between.
Why interfile taint is the hard and expensive part
Real applications do not put the source and the sink in the same function. A request parameter enters in a controller, gets passed into a service, stored on a DTO, read back by a repository and concatenated into a query four files away. An analyser that only reasons within one file sees none of that.
Here is the finding that matters, and it is checkable. As of 2026-10-18, cross-file taint analysis is proprietary-only across this market:
- Semgrep’s own documentation states plainly: “Semgrep open source software, Semgrep Community Edition (CE), can only analyze interactions within a single function” (docs.semgrep.dev, verified 2026-10-18). Cross-file analysis requires the proprietary Pro engine, enabled in a rule with
interfile: trueunder theoptionskey.semgrep ci --pro-intrafiledeliberately downgrades to single-file cross-function analysis when the full pass is too slow. - Opengrep, the LGPL-2.1 fork, ships
--taint-intrafilewith constructor and field-assignment tracking, inter-method taint flow and higher-order function support across 12 languages. That is a genuine match for--pro-intrafile. Its README does not claim interfile taint (verified 2026-10-18). - SonarQube’s taint analysis is not in Community Build either. Sonar’s own open source edition page lists taint analysis and secrets detection among the capabilities you get by upgrading from Community Build, which ships 21 languages and excludes C, C++, Swift, Dart, T-SQL and PL/SQL (sonarsource.com, verified 2026-10-18).
So: “interfile taint analysis plus an open source licence” does not exist in this market as of 2026-10-18. You can have one or the other.
One nuance most write-ups get wrong, and worth getting right: proprietary is not the same as paid. Semgrep’s hosted free tier lists cross-file analysis with the Pro engine at every tier including Free, which is capped at 10 contributors, 10 private repositories and 60 AI credits (semgrep.dev/pricing, verified 2026-10-18). You can get interfile taint for nothing. You cannot get it under a licence that lets you fork the engine.
| Engine | Licence | Single-file cross-function taint | Interfile (cross-file) taint |
|---|---|---|---|
| Semgrep CE (OSS CLI) | LGPL-2.1 | No - single function only, per Semgrep docs | No |
| Opengrep | LGPL-2.1 | Yes, --taint-intrafile | Not claimed in README |
| Semgrep Pro engine (hosted, incl. free tier) | Proprietary | Yes, default in Semgrep Code | Yes, interfile: true |
| SonarQube Community Build | LGPL-3.0 | Limited, no taint analysis | No |
| SonarQube Developer Edition and above | Commercial | Yes | Yes |
| CodeQL | Free for OSS, paid for private via GitHub Advanced Security | Yes | Yes |
All rows verified at vendor documentation on 2026-10-18.
How does static code analysis improve code quality?
Direct answer: It improves quality mainly by being consistent, not by being clever. A rule applied to every line of every pull request forever removes an entire defect class from your codebase and ends the recurring style argument in review. The measurable effect is fewer regressions of known categories, and reviewer attention redirected to design. Verified 2026-10-18.
The weak version of this claim is that static analysis finds bugs. It does, but a competent reviewer finds many of the same ones. The strong version is more specific and more defensible.
It converts a judgement call into a settled rule. Once “no string concatenation into SQL” is a rule that fails the build, it stops being a thing senior engineers argue about in review comments. The argument happens once, in the ruleset. That reclaimed review bandwidth is worth more than most individual findings.
It is uniform across the team and across time. A reviewer at 5pm on a Friday is not the reviewer they were on Tuesday morning. The analyser is. This is the entire basis of using it as a compliance artefact: you can show an auditor the ruleset, the gate configuration and the run history, and the chain is reproducible. You cannot do that with a probabilistic reviewer.
It is cheap at the point of detection. The cost curve on defects is real even if the specific multipliers quoted around it are mostly folklore. A null dereference caught in the editor costs a keystroke. The same bug caught in production costs an incident channel.
It gives you a trend line. Issue density, duplication percentage, cyclomatic and cognitive complexity, coverage on new code. These are weak as absolute targets and strong as derivatives. Nobody should care that the codebase has a technical debt ratio of 6%. Everybody should care if it went from 4% to 6% in a quarter.
What it does not do: find the bug that comes from misunderstanding the requirement. No rule describes “this discount applies to the wrong customer tier”. That gap is exactly where AI code review earns its place, and it is why we treat the two as peers.
How to prioritise static analysis findings
Direct answer: Prioritise on three axes multiplied together: severity, confidence, and blast radius. Fix high-severity findings with high confidence on code reachable from untrusted input first. Never prioritise by count. A backlog of 4,000 findings sorted by severity alone produces busywork, because severity is the vendor’s opinion and confidence is yours. Verified 2026-10-18.
The classic mistake is treating the finding count as the metric. Teams burn a quarter driving 4,000 findings to 3,000, which changes nothing about risk, because the 1,000 they fixed were the easy ones and the dangerous ones are still there.
Rank on three dimensions:
Severity is how bad it is if real. The vendor assigns this and it is a generic judgement made without knowledge of your system. A hardcoded credential is critical everywhere. “Prefer const over let” is not.
Confidence is how likely the finding is to be real in your code. Tools expose this unevenly: Semgrep rules carry explicit confidence metadata, SonarQube splits issues from security hotspots (which are explicitly “needs human review” rather than “is a bug”), and most linters expose nothing at all. Where the tool does not give you confidence, your own triage history does.
Blast radius is the one teams skip and the one that actually reorders the list. A SQL injection in an internal admin tool behind a VPN and SSO is not the same risk as the identical pattern in a public signup endpoint. Reachability from untrusted input, and the sensitivity of the data behind the sink, should dominate.
| Severity | Confidence | Blast radius | What to do |
|---|---|---|---|
| Critical | High | Internet-facing | Fix now, block the merge |
| Critical | High | Internal only | Fix this sprint, gate new occurrences |
| Critical | Low | Any | Triage by hand before anyone fixes anything |
| Medium | High | Internet-facing | Gate on new code, backlog the existing |
| Medium | High | Internal only | Autofix if available, otherwise leave it |
| Low | Any | Any | Do not create tickets. Enforce on new code only |
| Any | Consistently wrong | Any | Disable the rule, in the config, with a comment saying why |
That last row is the one people resist and it is the most important. A rule your team has dismissed ten times is not a finding, it is a config bug. Leaving it enabled teaches everyone that findings are optional, which destroys the value of every other rule. Disabling it in a tracked config file with a one-line rationale is a better engineering decision and a better audit artefact than a growing pile of inline suppressions.
For a deeper treatment of precision and noise on the AI side of the stack, see AI code review tools with low false positives.
How to set up static analysis rules for a codebase
Direct answer: Start with the vendor’s recommended ruleset, not the full catalogue. Commit the config to the repo so rule changes go through review. Enable security and correctness rules first, style last, and only after a formatter owns layout. Write custom rules only for patterns specific to your codebase. Verified 2026-10-18.
The default failure mode is enabling everything on day one, producing thousands of findings, and losing the team’s attention permanently. The order below avoids that.
Step 1: let a formatter take layout off the table. Prettier, Black, gofmt, rustfmt. Formatting is not an analysis problem and arguing about it inside a linter config is wasted effort. Run the formatter in a pre-commit hook and delete every layout rule from the analyser.
Step 2: enable the recommended set, not the full set. Every serious tool ships a curated default: eslint:recommended, Sonar way, Semgrep’s p/default, Ruff’s E,F selection. These exist because the vendor already did the noise triage for you. The full catalogue is for tuning later.
Step 3: commit the config to the repository. .eslintrc, .semgrep.yml, sonar-project.properties, qlty.toml, .deepsource.toml. A config in the repo means every rule change is a reviewed diff with an author and a rationale. A config in a vendor web UI means nobody knows who turned off the SQL injection rule in March.
Step 4: add security rules, scoped. Taint rules are where false positives concentrate because sanitiser recognition is imperfect. Enable them on the directories handling untrusted input before enabling them repository-wide.
Step 5: write custom rules for your own invariants. This is the highest-leverage and least-used capability in the category. Generic rules catch generic bugs. Only you can write “never call internalApiClient outside the services/ directory” or “every database migration must be reversible” or “no new usage of the deprecated LegacyAuth module”.
Semgrep is the easiest engine to author in, because a rule is YAML that looks like the code it matches:
rules:
- id: no-legacy-auth
pattern: LegacyAuth.$METHOD(...)
message: LegacyAuth is deprecated. Use AuthService. See RFC-114.
languages: [python]
severity: WARNING
SonarQube supports custom rules through plugins and, from Enterprise Edition upward, custom security-engine configuration for taint sources and sinks. ESLint has a mature plugin API. CodeQL rules are written in a dedicated query language and are the most powerful and the steepest to learn.
Step 6: tune the gate, then stop touching it. A ruleset that changes weekly is one nobody trusts. Review it quarterly, with the triage data from the previous quarter in front of you.
Static analysis rules for legacy codebases
Direct answer: Ratchet on new code only. Set the quality gate to evaluate the changed lines in the diff, not the repository, so new findings block the merge while inherited findings are reported and ignored. A clean baseline on new code beats a low total count, because the total will not move and the gate has to stay credible. Verified 2026-10-18.
This is the single most useful adoption tactic in static analysis, and most guides omit it entirely.
Point a modern analyser at a ten-year-old codebase and you get a five-figure finding count. The two instinctive responses both fail. Fixing them all is a quarter of work with no feature output and a real regression risk in code nobody remembers. Turning the gate off until the backlog is clear means it is never on.
The third option works: freeze the past, enforce the present.
Set a baseline. Nearly every tool supports this, under a different name in each: SonarQube’s “new code period” (defined as a previous version, a number of days, or a specific analysis), Semgrep’s --baseline-commit, ESLint’s --cache plus a suppressions file, DeepSource’s existing-issue baseline, Qlty’s diff-scoped analysis. Everything before the baseline is recorded and excluded from the gate.
Gate the diff, not the repo. A pull request fails if it introduces a new blocker or critical finding on a changed line. It does not fail because the file it touched already had 40 findings before anyone opened it. This is the difference between a gate the team respects and an override button the team has memorised.
Add coverage requirements on new code only. “80% coverage on changed lines” is achievable in a legacy repository on day one. “80% coverage overall” is a two-year project and will be quietly abandoned.
Attack the backlog by blast radius, not by count. Pick the authentication module, or the payment path, or whatever directory has the highest ratio of security findings to lines, and clear that one directory completely. One clean high-risk module is worth more than a 15% reduction spread evenly.
Expect the total to go up before it goes down, because you will keep adding rules. That is fine if the number you report to management is “new findings introduced this month”, not “total findings”. The total is a property of the codebase’s age. The delta is a property of your team’s discipline, and it is the only one you control.
Where AI review fits: determinism is the whole distinction
Direct answer: Static analysis is deterministic and auditable. AI review is probabilistic and better at semantics. Run both: the analyser as the merge gate, the AI reviewer as advisory commentary. Do not let an AI reviewer be your compliance evidence, and do not expect a ruleset to catch a misunderstood requirement. Verified 2026-10-18.
The properties that matter are not “old” and “new”. They are these.
Reproducibility. Run a static analyser twice on the same commit with the same ruleset and you get byte-identical findings. Run an AI reviewer twice on the same diff and you may not get the same comments. That is not a defect of AI review; it is a consequence of sampling from a model. But it decides which layer can be a merge gate and which layer can be evidence.
Explainability. A static finding traces to a rule ID, and the rule is readable. S2076, python.lang.security.audit.dangerous-subprocess-use. You can read the rule, disagree with the rule, and disable the rule. An AI finding traces to a prompt and a model version.
Coverage shape. A ruleset catches exactly what its rules describe, every time, with zero variance and zero imagination. An AI reviewer catches things no rule describes, inconsistently. Those are complementary distributions, which is why the stack that works is both.
Measurability. You can measure a static analyser’s behaviour yourself in an afternoon. For AI reviewers, you largely cannot, because no party without a commercial stake has published a false-positive rate, precision figure or noise measurement for any shipping commercial AI code reviewer (verified 2026-10-18). Independent academic benchmarks like SWE-PRBench and CR-Bench do publish false-positive data, but they measure bare frontier models in fixed harnesses, not products you can buy. The nearest thing to an exception is c-CRAB (arXiv 2603.23448), which evaluates four agentic tools and reports that “Considering the union across all four tools, 97 out of the 234 tests were passed by at least one tool (41.5%)” - and note that while five of its six authors are academic, one lists SonarSource as his affiliation, and Sonar sells SonarQube and acquired Gitar, a paid sponsor of this site, in May 2026. The paper carries a disclaimer that its conclusions do not represent SonarSource policy, and SonarQube is not among the tools it evaluates. We disclose it because we call out vendor-funded research everywhere else.
The practical architecture most mature teams land on, and the one I would build:
- Formatter and fast linter in the editor and pre-commit hook.
- Full deterministic analysis on the pull request, gated on new code.
- AI review on the same pull request, advisory, never blocking.
- Expensive cross-file taint and security passes nightly, reported as trend.
- DAST and dependency scanning against the deployed artefact.
Layers 2 and 3 are peers. Dropping layer 2 because you bought layer 3 is the mistake this cluster of the market is currently making.
How we verified this
Everything below was checked at the primary source on 2026-10-18.
What we verified at source:
- Semgrep CE’s single-function limit, the
interfile: trueoption key, and the--pro-intrafiledowngrade flag, at docs.semgrep.dev. - Semgrep’s free tier limits (10 contributors, 10 private repositories, 60 AI credits, no secrets detection) and Teams pricing at $30 per contributor per month for Code, at semgrep.dev/pricing.
- Opengrep’s LGPL-2.1 licence and
--taint-intrafilecapability, with no interfile taint claim in the README, at github.com/opengrep/opengrep. - SonarQube Community Build’s 21 languages, and that taint analysis, secrets detection and C/C++/Swift/Dart/T-SQL/PL-SQL support require a paid edition, at sonarsource.com.
- That Developer Edition is the minimum for branch and pull request analysis, and that Community Build cannot analyse pull requests, via Sonar’s own documentation query interface at docs.sonarsource.com.
- Codacy’s Developer free tier and Team pricing at $18 per developer per month billed yearly, capped at 30 developers, at codacy.com/pricing.
- DeepSource’s pay-as-you-go AI review billing at $8 per 10K processed LOC (Standard) and $15 per 10K (Advanced), at deepsource.com/pricing.
What we could not verify:
- JetBrains Qodana list prices. The pricing page renders client-side and returned no figures to us on 2026-10-18. We are not quoting a price for it.
- PVS-Studio prices. PVS-Studio publishes no list prices; its order page is a quote request form. Free licences for open source, students and Microsoft MVPs are advertised.
- Cppcheck Premium prices.
cppchecksolutions.com/pricing/redirects tocppcheck.com/pricing/, which returned 404 on 2026-10-18. - Any independent false-positive rate for a shipping commercial AI code reviewer. None exists. Anthropic publishes an under-1% false-positive claim for its own Code Review product; it is vendor-published and unreplicated, and we do not repeat it as fact.
- A published benchmark comparing self-hosted local-model review quality against cloud-tier review quality. We looked. There is not one.
What would change this article: an open source engine shipping genuine interfile taint analysis, or any party without a commercial stake publishing precision data on a commercial AI reviewer. Either would be the biggest story in this category. Neither has happened as of 2026-10-18.
Recommendations
Stated criteria: deterministic behaviour you can reproduce, a gate that can block a merge on new code only, a config that lives in the repository, and a licence you have actually read.
If you are starting from nothing on a multi-language repo: connect Codacy or DeepSource. Both auto-detect languages and ship opinionated defaults, and both get you to a working PR gate in under an hour. Codacy’s Developer tier is free forever; DeepSource is free for public repositories with 1,000 PR reviews per month.
If your main concern is security and you want rules you can author yourself: Semgrep. Rules are readable YAML, the hosted free tier includes the interfile engine up to 10 contributors, and you can run the CE binary in CI with no account at all if you accept the single-function limit.
If you need depth, a quality gate with history, and air-gapped operation: SonarQube. Budget for Developer Edition at minimum, because Community Build cannot analyse pull requests and that is the only place the gate matters.
If you are adopting on a legacy codebase: whichever tool you pick, the first configuration change you make is the new-code baseline. Ratchet on the diff. Everything else is secondary.
If you already run an AI reviewer and are wondering whether you still need this layer: yes, and the reason is not detection depth. It is that you can prove what the analyser did, and you cannot prove what the model did.
Further reading
- Static code analysis tools: the definitive guide - 20+ tools compared by type and methodology
- Best static analysis tools for finding code bugs: a buyer’s guide - organised by the decision you are making
- Best SAST tools, 32 reviewed
- SAST vs DAST - and where IAST and SCA fit
- What is a linter? - the fast single-file subset
- AI code review in CI pipelines - where each layer runs
- The AI code review price book 2026
- Language-specific: TypeScript, Go, Rust, C#, Kotlin, Swift, Scala
GitarComments are not enough
Gitar applies the fix, validates it in CI, and clears the queue.
See it on your repo Read our independent Gitar reviewFrequently Asked Questions
When should static analysis run in a CI pipeline?
Run it three times, with different strictness at each stage. In the editor and in a pre-commit hook, run only fast single-file rules so feedback arrives in under a second. On every pull request, run the full ruleset but scope the quality gate to changed lines only, so the build fails on new debt and not on inherited debt. On a nightly or weekly schedule, run the expensive cross-file and taint passes over the whole repository and publish the result as a trend, not as a build failure. The PR run is the enforcement point because it is the only moment a human is already reading the code and can act on a finding cheaply. See our guide to AI code review in CI pipelines for the full wiring.
Which static analysis tool is easiest to configure?
For a single language, the language-native linter is the easiest thing in the market: ESLint, Ruff, golangci-lint, Clippy and RuboCop all run from a single config file with sane defaults and no account. For a multi-language repository with no config work at all, Codacy and DeepSource both auto-detect languages on repository connect and ship opinionated default rulesets, and Codacy's Developer tier is free forever (verified 2026-10-18). Semgrep is the easiest tool to write your own rules in, because a rule is YAML that looks like the code it matches rather than an AST visitor in a plugin API. SonarQube is the hardest of the mainstream options to set up and the one with the deepest rule coverage, which is the trade you are making.
Static code analysis tools with code review automation
Four tools genuinely combine a deterministic analyser with automated review commenting on the pull request: DeepSource runs 5,000-plus deterministic rules across 30-plus languages before its AI agent sees the code, Codacy pairs its analysis engines with an AI Reviewer (cloud and GitHub only, so GitHub Enterprise Server customers structurally cannot get it), SonarQube posts quality gate status as a PR check from Developer Edition upward, and Qlty layers linting, duplication and maintainability analysis with autofixes. Semgrep comments on PRs through its hosted platform. Note that the deterministic layer and the AI layer fail differently, which is the point of running both.
AI code review vs static analysis
Static analysis is deterministic: the same code and the same ruleset produce the same findings every run, which is what makes it acceptable as an audit artefact and safe to use as a merge gate. AI review is probabilistic: it catches the subtle logic bug no rule describes, and it will not necessarily produce the same comment twice on the same diff. The honest framing is that they are peer layers with different failure modes, not old and new versions of the same thing. No party without a commercial stake has published a false-positive rate for any shipping commercial AI reviewer, so you cannot currently buy an AI reviewer on verified precision. You can verify a static analyser's behaviour yourself by running it twice.
What is the difference between static analysis and a linter?
A linter is the fast, single-file, rule-based subset of static analysis: it parses one file into an abstract syntax tree and matches patterns against it. Full static analysis adds data-flow analysis, which tracks how values move through a program, and taint tracking, which follows untrusted input from a source to a dangerous sink. The practical difference is that a linter can tell you a variable is unused and a data-flow analyser can tell you a request parameter reaches a SQL string without sanitisation. Linters run in milliseconds on every save. Cross-file taint analysis can take minutes on a large repository, which is why it is usually a CI job rather than an editor plugin.
Can static analysis prove my code has no bugs?
No, and any vendor implying otherwise is selling something. Static analysis is bounded by Rice's theorem: non-trivial semantic properties of programs are undecidable in general, so every practical analyser approximates. Analysers choose which way to be wrong. A sound analyser over-approximates and reports findings that cannot actually happen, which shows up as false positives. A complete analyser under-approximates and stays quiet about real bugs, which shows up as false negatives. Commercial tools tune aggressively toward fewer false positives because developer trust collapses faster from noise than from silence. That tuning decision is why two tools on the same codebase return different findings and why neither result is a proof.
Does static analysis need the code to compile?
It depends on the engine, and this decides how painful your setup is. Pattern-matching engines like Semgrep parse source directly and need no build, which is why they run in seconds on any checkout. Compiler-based analysers need a successful build: Coverity, Klocwork and clang-tidy work from the compilation database, SpotBugs analyses JVM bytecode, and SonarQube requires a build wrapper for C, C++ and Objective-C. Build-dependent analysers see more, because they have resolved types and the real call graph, but they also break whenever your build breaks, and reproducing a monorepo build inside a scanner container is a common reason static analysis rollouts stall.
How many false positives should I expect from static analysis?
There is no credible industry-wide number, and anyone quoting one is usually quoting a vendor. The rate depends almost entirely on the ruleset you enable, not on the tool. A default security ruleset on an unprepared codebase commonly produces findings at a rate teams describe as unworkable, while the same tool restricted to high-confidence rules can be near silent. The practical measure is not a percentage but a question: of the last 20 findings your team triaged, how many led to a code change? If it is fewer than half, disable rules until it is not. Measure it on your own repository, because the published numbers are measured on someone else's.
Should static analysis block a merge?
Yes, but only on new code. Blocking the merge is the whole reason to run the tool, because a finding that nobody has to resolve gets ignored within two sprints. The failure mode is blocking on the existing backlog, which makes every unrelated pull request fail and trains the team to use the override. Configure the gate on the changed lines of the diff: new blocker and critical findings fail the build, everything inherited is reported but not enforced. SonarQube calls this the new code period and it is the single most useful adoption setting in the category. Keep an audited override path, because an unoverridable gate is a gate that gets deleted.
Explore More
Tool Reviews
Free Newsletter
Stay ahead with AI dev tools
Weekly insights on AI code review, static analysis, and developer productivity. No spam, unsubscribe anytime.
Join developers getting weekly AI tool insights.
Related Articles
Every AI Code Review Benchmark, Compared (2026)
All 19 AI code review benchmarks: who published each one, whether they sell a reviewer, sample size, metric, and who won. Verified 18 October 2026.
October 18, 2026
guideAI Code Review in CI/CD Pipelines: Where to Run It (2026)
AI code review tools that work with existing CI pipelines, in three architectures. Real GitHub Actions, GitLab CI and Jenkins config, plus what each one costs.
October 18, 2026
guideAI Code Review False Positive Measurement: Our Method
The ACR Noise Index protocol: how we will measure AI code review false positives. Taxonomy, PR selection, blind classification, variance. Pre-registered 2026-10-18.
October 18, 2026
Semgrep Review
SonarQube Review
Codacy Review
DeepSource Review
Qlty Review
Snyk Code Review