Static Analysis

Method

Inspection that reads code as written, without running it, to find likely bugs, security vulnerabilities, and rule violations.

Why it exists

For a long time there were two ways to find problems in code: run it, or have a person read it.

Both miss in predictable places. Tests only confirm the paths they actually walk. A test that covers three of ten branches says nothing about what lives in the other seven, and coverage numbers make that gap look smaller than it is. Code review, the practice of having a colleague read the change before it merges, loses attention past about 300 lines and misses the same category of mistake over and over.

So the familiar scene goes like this: code that passed review with an all-green test run ships, then breaks on a case where a value is missing. Looking back, the tests never walked that path and the reviewer missed it somewhere around the twentieth file. Asking people to read more carefully does not reduce this category, because the failure is attention, not skill.

Static analysis fills the gap by having a machine read the structure of the code instead of executing it. A variable that might be empty used without a check, a resource that never gets closed, an outside value dropped straight into a query — anything recognizable by shape gets found, on every file, every time.

How to run it

It starts with choosing what to check. Turn everything on at once and thousands of warnings appear, after which nobody reads any of them.

  1. Pick the rule sets. Type problems like possible emptiness, common bug shapes, security patterns, and license compliance for the outside code you depend on — enable what you need now.
  2. Run it once against the existing code to establish a baseline. 2,800 findings in a two-year-old repository is normal. Deciding to fix all of them before starting means never starting.
  3. Gate only new code. Record the existing 2,800 as a list and block merges when a high-severity finding appears on a line added or changed from now on. Existing code gets cleaned up whenever someone opens that file for another reason, which spreads the work across the months it takes rather than into one block.
  4. Attach it to CI/CD, the system that automatically verifies changes and carries them through to deployment. Put the check there and it runs whether or not anyone remembers. Left as something each person runs locally, it gets skipped on busy days.
  5. Handle false positives. Where something got flagged but is fine, add a suppression and one line explaining why. Suppressions without reasons pile up, and the next person can't tell whether removing one is safe. A rule producing mostly false positives should be turned off entirely rather than suppressed case by case.
  6. Raise the bar each quarter. Promote a warning-only rule to a blocking one, or enable a new set. Starting strict rarely survives; ratcheting up does, because each step is small enough that nobody argues for turning the whole thing off.

When it fits and when it doesn't

It earns its place on the categories a machine can judge: values that might be empty, unreachable code, unclosed files, external input reaching a query unchecked. The benefit grows with size. Nobody reads a 3,000-file repository end to end, and the machine reads all of it every twenty minutes, including the files nobody has opened in a year.

What it misses is equally clear. Anything you have to run to learn sits outside the method — why the second screen is slow, a calculation error that only appears with real data, a feature built on a misread requirement. Code with flawless syntax that obeys every rule while doing the wrong thing passes.

False positives and false negatives are limits too. When 20 percent of findings turn out not to be problems, people start ignoring the whole report, and from then on the check runs and accomplishes nothing. In the other direction, because it reads shape, problems that require following execution slip through quietly.

Tools with overlapping roles differ in reach. A linter, which automatically catches violations of coding rules, is one branch of static analysis; a formatter, which arranges code to a fixed style, makes no judgment about correctness at all. Whether the design suits the problem, and whether the feature is needed, still belong to code review.

How it sounds at work

"Static analysis threw twelve possible-null warnings, so the merge is blocked."

Reporting that the check stopped the change from merging.

"We listed the existing warnings and configured it to gate new code only."

Choosing to stop the count from growing rather than fixing everything at once.

"This rule has too many false positives. Better to turn it off."

A proposal to remove a rule that makes the whole check untrustworthy.

At Pagedeck, Theo saw 2,800 findings on day one and considered reverting the whole thing. Fixing them all needed two weeks with everything else stopped. Instead the existing count went into a stored list, and gating applied only to high-severity findings on changed lines.

Four months later the remaining warnings were down from 2,800 to 1,900. No cleanup sprint happened; the reduction came from handling a file's warnings whenever someone opened it to change a feature. Over the same period, production errors caused by missing values fell from six a month to one, which was the argument that kept the tool installed.

What people get wrong

The most common misreading is that passing the check means the code is safe. Everything shows green, so it feels that way, especially to whoever approves the release. But passing means only that no problem of a shape this tool knows is present. A feature built on a misread requirement, a bottleneck that appears only at real volume, a conflict between two features running together — none of those are in scope. Static analysis, tests, and code review are nets for different categories, and no one of them substitutes for the others. Teams that drop review after adopting analysis usually rediscover this within a quarter.

Believing the warning count has to reach zero is the other one. The number is visible, so it reads like a target. Reaching zero in an older repository means halting feature work, and since that can't happen, the usual outcome is loosening the rules until the number is zero. The count reads zero and the check finds nothing either. The number worth watching in practice is not how many remain but how many were added, measured per week against the lines that changed.

Easily confused with

TermWhat it looks atHow it differs
LinterViolations of the coding rules you setIt's one branch of static analysis. It asks whether code matches the rules, while static analysis extends to inferring execution outcomes, like possible emptiness or a vulnerable pattern

Where to go next

Last updated 2026-09-16