Code coverage is a measurement of how much of your source code executes while your test suite runs. Tools like Istanbul (nyc), Coverage.py, and JaCoCo instrument your code, record which lines fire during a test run, and report the result as a percentage. It is one of the oldest metrics in software testing, and also one of the most misread: useful for finding untested gaps, misleading when treated as a proxy for quality.
What changed in 2026
- AI-assisted test generators now optimize directly for coverage numbers, which revived the old gaming problem — an assistant asked to "raise coverage" will happily write assertion-free tests unless you explicitly ask for meaningful checks.
- Branch and path coverage became the CI default at most teams that had previously tracked only line coverage, after enough postmortems traced a bug back to an untested
else branch.
- Coverage maps started feeding test generation directly, with fuzzing-style tools steering new inputs toward code the existing suite never reaches.
- Mutation testing adoption grew specifically because coverage lost credibility as a quality signal — see what mutation testing is in 2026 for the sharper follow-up metric.
How coverage is actually measured
An instrumented build wraps each line, branch, or statement with a counter. When the test suite runs, the tool tallies which counters incremented and divides by the total. The result depends entirely on which granularity you measure:
| Coverage type |
What it counts |
What it misses |
| Line / statement |
Lines executed at least once |
Untested branches inside a single line |
| Branch |
Both true and false paths of a conditional |
Combinations of multiple branches together |
| Function |
Whether a function was called at all |
Everything about what happened inside it |
| Path |
Every distinct route through a function |
Explodes combinatorially in real code |
Most teams default to line and branch coverage together; path coverage is usually too expensive to track outside small, critical functions.
Why a high percentage can still lie to you
Coverage tells you a line ran. It says nothing about whether the test checked the outcome. A test that calls calculateTotal(cart) and asserts nothing still marks every line inside that function as covered. This is the central limitation: coverage is a necessary condition for a good test suite, not a sufficient one. A codebase can sit at 95 percent coverage and still ship broken releases weekly if the assertions are weak or missing.
Reading a coverage report usefully
- Sort by lowest-covered files first. These are your real gaps, not the aggregate percentage.
- Look at red branches, not red lines. An uncovered
if branch usually represents an untested edge case, which is a higher-value find than an uncovered log statement.
- Treat drops in coverage on a diff as a prompt to look, not a hard gate. A five-line utility function with no branches does not need a dedicated test.
- Cross-check against what a flaky test is in 2026 — a suite riddled with flaky tests can report high coverage while being untrustworthy in practice.
Common mistakes
Setting a single global threshold, like 80 percent, and enforcing it everywhere. Critical payment logic and a generated types file do not deserve the same bar. Set thresholds per package or module.
Writing tests to move the number, not to catch bugs. This produces exactly the assertion-free tests that inflate coverage without adding safety.
Ignoring coverage trends over time. A single snapshot matters less than whether new code consistently ships with tests, which coverage-on-diff tooling can enforce far better than a global percentage.
What to skip
- Chasing the last 5-10 percent of coverage on error-handling branches and generated code that rarely changes and rarely breaks.
- Coverage as a performance review metric for individual engineers — it is trivially gameable and measures the wrong thing.
- Comparing coverage percentages across projects with different languages, frameworks, or test philosophies — the number is not standardized enough to be comparable.
FAQ
What is a good code coverage percentage?
There is no universal number. Most mature teams land between 70 and 90 percent on business logic, with lower expectations for UI glue code and generated files. The trend and the location of gaps matter more than the aggregate figure.
Does 100 percent coverage mean the code is bug-free?
No. Coverage only confirms a line executed, not that the test verified correct behavior. Bugs regularly ship from fully covered code with weak assertions.
What is the difference between code coverage and test coverage?
They are often used interchangeably, but code coverage is specifically about which source lines executed. Test coverage sometimes refers more broadly to which requirements or scenarios are tested, which coverage tools cannot measure directly.
Should I block pull requests that lower coverage?
Blocking on a small drop from removing dead code is counterproductive. Blocking when new, untested logic ships without any test is usually worth enforcing.
Where to go next