guide

What Is Code Coverage? Line, Branch and Mutation Explained (2026)

A practical guide to code coverage - line vs branch vs mutation coverage, realistic targets, why 100 percent is a trap, and how to gate coverage in pull requests.

Published:

What is code coverage?

Code coverage is the percentage of your source code that gets executed while your automated test suite runs. A coverage tool instruments your code, runs your tests, records exactly which parts were reached, and reports the result as a percentage. If your suite executes 850 out of 1,000 lines, you have 85 percent line coverage. It is one of the few objective, automatable signals you have about how thoroughly your code is tested.

But the single most important thing to understand about coverage is what it does not measure. Coverage tells you which code your tests touched. It says nothing about whether those tests actually verified the right behavior. A test that runs a function but never asserts anything about the result still counts toward coverage. That gap between execution and verification is the source of almost every mistake teams make with this metric. The starting point is our code coverage glossary entry.

The types of code coverage

Coverage is not a single number. Different coverage types answer different questions, and the difference between them is where the metric earns its value.

Line coverage

The simplest and most common. Did each line of code execute at least once? It is easy to understand and easy to game. Consider:

def apply_fee(amount, is_member):
    fee = 0
    if is_member:
        fee = amount * 0.01
    return amount + fee

A single test calling apply_fee(100, True) executes every line - 100 percent line coverage. But you never tested the non-member path where is_member is False. Line coverage says you are done. You are not.

Branch coverage

Branch coverage fixes exactly that blind spot. It asks whether every possible outcome of each decision executed - both the true and false side of every if, every case of a switch, each side of a ternary. In the example above, branch coverage would report 50 percent, correctly flagging that the false branch of the if was never taken. This is why branch coverage is a far better gate than line coverage - it maps directly to the cyclomatic complexity of your code, since each independent path needs its own test.

Function and statement coverage

Function coverage counts whether each function was called at all - a coarse smoke-test signal. Statement coverage is similar to line coverage but counts individual statements, so multiple statements on one line are tracked separately. Both are useful context but weaker gates than branch coverage.

Mutation coverage

The most rigorous and most overlooked. Mutation testing deliberately introduces small bugs into your code - flipping a > to >=, replacing a + with a - - and checks whether your tests catch the change by failing. If a mutant survives, your tests executed that code but did not actually verify it. Mutation coverage measures assertion quality, which is precisely what line and branch coverage cannot. It is slower to run, so most teams reserve it for critical modules.

Coverage typeQuestion it answersStrictness
LineDid this line run?Low
StatementDid this statement run?Low
FunctionWas this function called?Low
BranchDid both sides of each decision run?Medium
MutationWould my tests catch a bug here?High

What is a realistic coverage target?

The most common mistake in this whole topic is mandating 100 percent. It sounds rigorous. It is actively harmful.

Google’s widely cited internal guidance treats 60 percent as “acceptable,” 75 percent as “commendable,” and 90 percent as “exemplary,” while explicitly cautioning against enforcing a single hard number across all code. For most application codebases, 70 to 85 percent branch coverage is the sweet spot - high enough to catch real regressions, low enough that you are not writing pointless tests for trivial getters and generated boilerplate.

The right target is risk-weighted, not uniform. Payment processing, authentication, and data-integrity code deserve 90 percent or higher plus mutation testing. An internal admin dashboard does not. Averaging them into one company-wide mandate produces the worst of both worlds - undertested critical paths and overtested trivia.

Chase coverage on the code where a bug would hurt most, and stop chasing it where a bug barely matters.

Why 100 percent is a trap

Because coverage measures execution, you can hit 100 percent with tests that verify nothing:

// 100% coverage, zero verification
test("calculateTotal runs", () => {
  calculateTotal([{ price: 10 }, { price: 20 }]);
  // no expect() - the test can never fail
});

This test executes every line of calculateTotal and contributes full coverage, yet it would pass even if the function returned the wrong number, threw, or returned undefined. A team optimizing for a 100 percent target is incentivized to write exactly these assertion-free tests. That is why high coverage with weak assertions is more dangerous than honest, lower coverage - it manufactures confidence that is not real. Mutation testing is the antidote, because a mutant introduced into calculateTotal would survive this test and expose it.

How to measure and gate coverage in pull requests

Measuring coverage locally is easy. The leverage comes from enforcing it on every pull request so coverage cannot silently erode. Two rules make this work in practice.

First, gate on coverage of new and changed code, not the whole project. Demanding that a 5-year-old codebase jump to 80 percent overnight is impossible and demoralizing. Demanding that every new pull request cover 80 percent of the lines it adds is achievable and stops the bleed. This “new code coverage” gate is the single highest-leverage setting.

SonarQube static analysis tool homepage screenshot
SonarQube homepage

SonarQube ingests coverage reports from your test runner and enforces a quality gate on new-code coverage, blocking merges that fall below your threshold and decorating the pull request with the exact uncovered lines. Codacy tracks coverage across 49 languages alongside its quality and security analysis, with the same new-code gating model.

Second, treat missing coverage as a prompt to generate tests, not just to complain. This is where AI test tools change the workflow. Qodo pairs its PR review with automatic test generation - it identifies the coverage gaps a change introduces and writes the unit tests to fill them, so the reviewer sees proposed tests rather than a red coverage number. DeepSource reports coverage among its analyzers with the same PR gating. For the full field, see our guides to the best code test coverage tools and the best AI test generation tools.

Common mistakes with code coverage

  • Mandating a single percentage everywhere. Risk-weight the target instead.
  • Gating on line coverage. Use branch coverage - line coverage hides untested decision paths.
  • Rewarding coverage without assertions. Spot-check with mutation testing on critical code.
  • Gating the whole project instead of the diff. Gate new-code coverage so legacy code does not block progress.
  • Reading coverage in isolation. Pair it with complexity - a complex function with high coverage but weak assertions is still a risk worth reviewing by hand.

Conclusion

Code coverage is a genuinely useful signal, but only when you read it correctly. It measures which code your tests execute, never whether those tests verify the right thing. Prefer branch coverage over line coverage, target a risk-weighted 70 to 85 percent rather than a blanket 100 percent, and reserve mutation testing for the code where a silent bug would hurt most. Then gate coverage on new code in every pull request with a tool like SonarQube or Codacy, and let a test generator like Qodo turn the gaps into proposed tests. Coverage is a floor, not a finish line.

Further reading

Sponsored Why?
Gitar logoGitar

Comments are not enough

Gitar applies the fix, validates it in CI, and clears the queue.

See it on your repo Read our independent Gitar review

Frequently Asked Questions

What is code coverage?

Code coverage is the percentage of your source code that is executed while your automated tests run. A coverage tool instruments the code, runs the test suite, and records which lines, branches, or functions were reached. If 800 of 1,000 lines run during testing, line coverage is 80 percent. It measures what your tests touch, not whether they actually verify correct behavior.

What is a good code coverage percentage?

There is no universal number, but 70 to 85 percent is a realistic and healthy target for most application code. Google's engineering guidance treats 60 percent as acceptable, 75 percent as commendable, and 90 percent as exemplary, while explicitly warning against mandating 100 percent. The right target depends on risk - critical payment or auth code deserves higher coverage than a rarely used admin screen.

What is the difference between line and branch coverage?

Line coverage counts whether each line ran. Branch coverage counts whether each possible outcome of every decision ran - both the true and false side of an if, every case of a switch. Branch coverage is stricter and more meaningful, because a single test can give a line 100 percent line coverage while leaving one side of its condition completely untested. Prefer branch coverage as your gate.

Why is 100 percent code coverage a bad goal?

Because coverage measures execution, not verification. You can reach 100 percent coverage with tests that have no assertions at all - they run every line but check nothing. Chasing 100 percent also pushes teams to write low-value tests for trivial getters and generated code, spending effort where bugs rarely live. High coverage with weak assertions gives false confidence, which is more dangerous than honestly lower coverage.

Explore More

Free Newsletter

Stay ahead with AI dev tools

Weekly insights on AI code review, static analysis, and developer productivity. No spam, unsubscribe anytime.

By subscribing you agree to receive the weekly newsletter. Unsubscribe in one click, any time. See our privacy policy.

Join developers getting weekly AI tool insights.

Related Articles