Static Code Analysis: How It Works, Techniques & Tools

A pull request sails through code review, passes every unit test, and merges cleanly. Three weeks later, it's the root cause of a production outage: a hardcoded credential nobody caught, a null dereference that only triggers on one input path, a SQL query built from unsanitized string concatenation. None of that required running the application to find. It was sitting in the source the whole time.
That's the gap static code analysis exists to close. This guide covers what it actually does, the specific techniques underlying the label, and what it catches versus what it structurally can't catch. It also covers how to wire it into your development lifecycle and where it hits a ceiling that most write-ups skip: code-level rules have no equivalent at the architecture level, which is where many of the expensive problems actually start.
What Is Static Code Analysis?
Static code analysis is the practice of examining source code for defects, vulnerabilities, and quality issues without executing it. Many static analyzers parse your code, build an internal model of its structure, and check that model against a set of rules. They flag violations before the program runs. That's the core mechanic behind everything from a linter catching an unused variable to a scanner flagging SQL injection risk.
The term overlaps heavily with SAST (Static Application Security Testing), and in casual conversation, people use them interchangeably, though they're not quite the same scope. Static code analysis is the broader practice, covering code quality, style, complexity, and correctness, as well as security. SAST is the security-specific subset, concerned with vulnerabilities an attacker could exploit. A tool like ESLint, doing style checks, is static analysis but not SAST; a tool like Semgrep, flagging an injection pattern, does both at once, though its open-source, pattern-matching engine isn't equivalent to a full interprocedural enterprise SAST platform in every configuration.
Static Code Analysis vs. Static Application Security Testing (SAST)
Every SAST tool is a static analyzer, but not every static analyzer is a SAST tool. A rule engine flagging cyclomatic complexity or a missing docstring is static analysis with no security angle; one flagging unescaped user input reaching a database query is SAST. Most production toolchains run both side by side rather than picking one.
How Static Code Analysis Works
Under the hood, static analyzers follow a fairly consistent pipeline, whether the tool is a five-minute linter or an enterprise SAST platform. Source code goes in, a structural model comes out, and rules get applied to that model rather than to raw text.
Step 1: Parsing and building the Abstract Syntax Tree (AST)
The analyzer first parses your source into tokens, the way a compiler's front end does. OWASP describes lexical analysis as converting source code syntax into tokens "in an attempt to abstract the source code and make it easier to manipulate." From those tokens, it builds an Abstract Syntax Tree (AST): a hierarchical structure representing the code's grammar, where this node is a function declaration and that one's a conditional. Formatting and comments are usually ignored during semantic analysis, though some tools preserve them for other purposes.
Most tools also derive a Control Flow Graph (CFG) from the AST at this stage. A CFG represents the code as nodes and directed edges, where OWASP defines a node as a basic block and edges as the jumps between them. That structure is what lets an analyzer reason about paths through the code, not just individual lines.
Step 2: Applying rule sets and checkers
With the AST and CFG built, the analyzer walks the structure and applies its rule set, ranging from simple pattern matches (does this call eval() on unsanitized input?) to rules that track data as it moves through the program. This is also where tool quality diverges the most: a large, well-maintained ruleset catches more, while a thin one gives you false confidence.
Step 3: Reporting findings and remediation guidance
The last step turns internal findings into something a developer can act on: a file, a line number, the violated rule, and, ideally, a suggested fix. Tools that stop at "line 47, rule violated" tend to get ignored. The ones that survive in a team's workflow explain why a pattern is risky, not just that it matched a signature.
Core Static Code Analysis Techniques
"Static analysis" is really an umbrella over several distinct techniques, and most commercial tools combine more than one. Knowing the difference matters because each technique has a different blind spot.
Rule-based and pattern-based analysis
The cheapest technique to implement, and the easiest to reason about. The analyzer has a library of known-bad patterns and flags any match: a function call, a string literal shape, a syntax construct. Fast, with few false positives for well-defined patterns, but it only catches what someone has already thought to write a rule for.
Data flow and taint analysis
Taint analysis is where static analysis starts to look less like search-and-replace and more like reasoning. OWASP defines it as identifying variables "tainted" with user-controllable input and tracing them to a "sink," a vulnerable function. If a tainted value reaches that sink without being sanitized, the analyzer flags it. That's the mechanism behind most SQL injection and cross-site scripting detection: the tool doesn't need to run the code; it just traces the path from source to sink.
Data flow analysis is the broader technique underneath taint tracking. It estimates how data could move and transform within a program without ever executing it, mapping the possible value flows the code could produce while remaining in a static, non-executing state. It's a neat way of describing what makes static analysis useful: it infers behavior the program hasn't performed yet.
Control flow analysis
Where data flow analysis follows values, control flow analysis follows paths. It maps every route execution could take through a function using the CFG built during parsing. A basic block, per OWASP's definition, is a straight-line sequence of instructions where control enters only at the top and exits only at the bottom. Stitching those blocks into a graph lets an analyzer catch unreachable code or a branch that never returns, reasoning about the shape of a whole function rather than one line at a time.
Lexical analysis
The shallowest but fastest technique: convert the source into tokens and match against forbidden constructs directly, without needing the full AST or CFG. A hardcoded secret or a banned import doesn't require understanding data flow, just recognizing the token pattern, which is why lexical-only linters can run on every keystroke while a full taint-tracking scan takes minutes.
Complexity and code duplication analysis
Not every finding is a bug. Complexity analysis flags functions that have become hard to test or reason about, even when every line is technically correct, using metrics such as cyclomatic complexity (the number of independent paths through a function). Duplication analysis finds copy-pasted blocks that should probably be one shared function. Neither catches a security vulnerability, but both are leading indicators of where bugs accumulate later.
What Static Code Analysis Catches (and What It Misses)
Common issues detected
Static analysis is strong for anything directly visible in the source, without needing to know what the program does at runtime. Taint analysis catches SQL injection risk. Lexical and pattern matching, often through dedicated static secret scanning rather than general SAST rules, catch hardcoded secrets and API keys. Control flow analysis catches dead code and unreachable branches. In C and C++, tools that track allocation and deallocation calls can flag possible memory leaks, double frees, and use-after-free patterns, though runtime and dynamic tools are often better at proving actual memory behavior. And because Terraform and Kubernetes manifests are themselves parseable, static text, the same techniques extend to misconfigurations in Infrastructure as Code files.
Many of the weakness categories that static analysis is built to catch map directly onto MITRE's CWE Top 25, the annually updated ranking of the most common and impactful software weaknesses behind that year's CVE records. Injection flaws, missing authentication checks, and improper input validation are recurring entries on that list. They're exactly the pattern-matchable, trace-the-data-flow class of problem that static tools are good at.
Known limitations
Static analysis has a structural ceiling, worth naming plainly instead of glossing over. OWASP's own writeup is pretty blunt about false positives: a tool "will often produce false positive results" partly because it "cannot be sure of the integrity and security of data as it flows through the application from input to output." That's especially true when code touches closed-source components or external systems it can't see into. False negatives cut the other way: a tool with no knowledge of the runtime environment can miss a vulnerability that only manifests under a specific configuration.
The other limitation matters more here: even tools that scan Infrastructure as Code, Kubernetes manifests, or CI configs are checking static definitions rather than live system state, so they struggle with configuration drift and architectural intent unless that context is explicitly modeled. A static analyzer has no runtime visibility and no model of your business logic. It can tell you a function is reachable; a standard, code-only static analyzer can't tell you whether that function should exist given what the rest of your system already does. That gap, code-level correctness versus system-level intent, is where technical debt and architectural drift tend to accumulate quietly.
Static analysis is well suited to code-level defects, but there's a set of questions it should not be relied on to answer:
- Business-logic validation
- Authorization design correctness
- Runtime configuration
- Dependency behavior
- Architectural duplication
- Production-state drift
Static vs. Dynamic Code Analysis: Where This Fits
Static analysis examines source code without running it. Dynamic analysis does the opposite: it runs the application and observes actual behavior, which is how it catches memory leaks, race conditions, and runtime exploits that only appear once code executes against real inputs. Static is fast and broad, scanning every line, including cold paths a test suite never exercises. Dynamic testing is slower and narrower, but it's the only way to prove a vulnerability is exploitable in a running system rather than merely theoretically present in the source.
Most mature teams run both, since each covers gaps the other leaves open. For the fuller trade-off breakdown, coverage, cost, required skill set, and a head-to-head comparison table, see our guide to dynamic vs. static code analysis.
How to Integrate Static Code Analysis Into Your Development Lifecycle
Static analysis delivers the most value the earlier it runs, since the cost of fixing a flaw climbs the further it travels through the SDLC. In practice, that means running it at three separate points.
In the IDE. Real-time feedback while you're still typing is the cheapest place to catch an issue, since the developer already has full context loaded. Lightweight lexical and rule-based checks run here; a full taint-tracking scan is usually too slow for keystroke-level feedback.
In code review and pull requests. A fuller scan runs against the diff, surfacing findings as PR comments before a reviewer opens the file, catching what IDE-level checks missed.
In the CI/CD pipeline. The last gate before merge or deploy runs the complete ruleset, including slower data-flow and taint passes too disruptive earlier. Many teams fail to build on high-severity findings while letting lower-severity ones surface as warnings, so the pipeline blocks what matters without getting noisy enough to be ignored.
The pattern across all three is that static analysis shifts from lightweight and instant to comprehensive and slower as code moves toward production. That's the practical meaning of "shift left." Catching the same bug in the IDE instead of in production is the difference between a developer fixing something they're already looking at and an incident call three weeks later.
Static Code Analysis Tools to Know
The tool landscape splits roughly into open-source engines, security-focused commercial platforms, and IDE-native scanners. None of these are architecture tools; they operate on code, which is the distinction the next section builds on.
Licensing is worth double-checking before you commit. SonarQube's Community Build and IDE plugin are free and open source, but Server and Cloud beyond that are commercial, with Cloud free only for qualifying open-source or small private projects. CodeQL is similar: free for research and open-source analysis, but private or commercial CI use needs a GitHub Advanced Security license. Assume "open source" claims are partial until you've checked current terms yourself.
Static Code Analysis and Architectural Governance
Static code analysis checks your code against coding rules: a rule for hardcoded secrets, a rule for SQL injection patterns, a rule for unreachable branches. It operates on a single file or repository at a time, so it structurally cannot check your code against architectural rules. Does this new service duplicate a capability that already exists elsewhere? Does this call bypass the API gateway that every other service goes through? Does this data pipeline conflict with one already running in another region?
That's not a tooling gap that a better SAST product fixes. It's a category difference. A linter can tell you a function is well-formed; nothing in a standard static analysis toolchain can tell you whether that function should exist given the rest of your system, because that answer depends on architectural context, which the tool was never given.
This is the gap we built Catio to close. Catio is the Architecture IDE, and its Compound stage does for architecture roughly what a SAST pipeline does for code, an analogy, not an equivalence: rather than applying line-level static rules, it reasons over architecture decisions, dependencies, and system context to track how the system evolves and surface drift from intent. Our platform doesn’t replace coding IDEs. It helps to define what they execute. Static analysis governs what a function does; Catio governs whether the system it's part of still makes sense.
Where a static analyzer flags a line number and a rule ID, Catio's reasoning agent, Archie, can reason about why an architectural pattern was chosen, what trade-offs it implies, and what breaks if you change it. A growing share of code is now written by AI coding agents in tools like Claude Code. Catio's MCP integration lets those same agents query connected architecture data directly from the editor. Guardrails appear in the same session in which the code is written, not in a separate review step days later. For the fuller case on treating architecture with the same rigor as code, see our primer on architecture as code.
Conclusion
Static code analysis is a mature approach to catching code-level problems, hardcoded secrets, injection risks, and dead code before they ship. It's also one layer below where a lot of the expensive problems start. A clean static analysis report tells you your code follows the rules it was checked against; it says nothing about whether the system that code is part of still matches what your team decided to build, and that's the layer where undocumented decisions and architectural drift quietly compound.
If your team already runs static analysis in CI and still gets surprised by architecture-level issues, that's not a static analysis failure; it's a sign the two layers need to work together. Catio applies the same "check continuously against standards" logic to the architecture layer, so the decisions behind your system stay as enforceable as the code that implements them.
Frequently Asked Questions
What is static code analysis?
Static code analysis is the process of examining source code for bugs, security vulnerabilities, and quality issues without executing the program. It works by parsing code into a structural model, typically an Abstract Syntax Tree, and checking that model against a set of rules.
Is SonarQube a SAST or DAST tool?
SonarQube is a static analysis tool, and its security-focused rule sets fall under SAST (Static Application Security Testing). It doesn't run your application, so it isn't a DAST (Dynamic Application Security Testing) tool.
What is the difference between static and dynamic code analysis?
Static analysis examines source code without running it, so it can scan every line, including code paths a test suite never exercises. Dynamic analysis runs the application and observes actual behavior, which is the only way to catch issues such as memory leaks or exploits that only appear at runtime.
What are the three main types of static code analysis?
There's no single official taxonomy, but the techniques that come up most often are pattern-based analysis (matching known-bad code shapes), data flow and taint analysis (tracing how values move through a program), and control flow analysis (mapping the possible execution paths through code).

