Skip to main content

Code Coverage

November 10, 2025By The Art of CTO15 min read
...
metrics

Track the percentage of code tested by automated tests. An essential quality metric, but remember: 100% coverage doesn't mean bug-free code.

Type:quality
Tracking: daily
Difficulty:easy
Measurement: Percentage of code executed by tests
Target Range: Good: 70-85% | Excellent: 80%+ | Diminishing returns: > 90%
Recommended Visualizations:gauge, line-chart, heat-map
Data Sources:Jest, pytest, JaCoCo, Istanbul, Coveralls, Codecov

Overview

Code Coverage measures the percentage of your codebase executed by automated tests. It's an important quality metric that indicates how much of your code is verified by tests, but it's not a silver bullet—high coverage doesn't guarantee bug-free code.

Why It Matters

  • Quality indicator: More coverage generally means fewer bugs
  • Confidence: High coverage enables confident refactoring
  • Regression prevention: Tests catch broken functionality
  • Documentation: Tests serve as executable specifications
  • Onboarding: New engineers learn codebase from tests
  • Change safety: Deploy with confidence

Types of Coverage

1. Line Coverage (Most Common)

Percentage of code lines executed by tests

Example:
Total lines: 1,000
Lines executed: 800
Line Coverage: 80%

2. Branch Coverage (More Thorough)

Percentage of conditional branches tested

Example:
if (user.isPremium || user.isTrial) {
  showFeature();  // Branch 1
} else {
  showUpgrade();  // Branch 2
}

Full branch coverage: Test both true and false paths

3. Function Coverage

Percentage of functions/methods called by tests

Good for: Identifying completely untested functions

4. Statement Coverage

Percentage of executable statements tested

Similar to line coverage but more granular

1. Gauge (Current Status)

Best for: Executive dashboards

Gauge ranges:
- Excellent: 80-100% (green)
- Good: 70-80% (blue)
- Fair: 60-70% (yellow)
- Poor: < 60% (red)

🎯 Current Coverage

Code Coverage

82%
Excellent (82.0%)
0%100%

Current coverage of 82% places the codebase in the Excellent category. Focus on maintaining this level while ensuring test quality remains high.

2. Line Chart (Trend Over Time)

Best for: Tracking improvement initiatives

Y-axis: Coverage %
X-axis: Time (weeks/months)
Target line: 80%
Annotations: Major refactors, new modules

📈 Coverage Improvement Over Time

Sample data showing steady improvement from 62% to 82% coverage over 6 months. The green dashed line represents the target threshold (80%). Continue enforcement on new code to maintain this trajectory.

3. Heat Map (File-Level Coverage)

Best for: Identifying gaps

Files displayed as colored blocks:
- Dark green: 90-100% coverage
- Light green: 70-90%
- Yellow: 50-70%
- Orange: 30-50%
- Red: < 30%

4. Bar Chart (Module Comparison)

Best for: Comparing components

Y-axis: Coverage %
X-axis: Modules/Services
Bars: Coverage per module
Target line: 80%

📊 Coverage by Module

Business Logic has the highest coverage (92%) which is excellent for critical code. UI Components are below target at 68%—consider increasing coverage for user-facing functionality. Data Access layer could also benefit from more integration tests.

Target Ranges

General Guidelines

| Level | Coverage | Recommendation | |-------|----------|---------------| | Minimum | 60% | Not sufficient for production | | Good | 70-80% | Acceptable baseline | | Excellent | 80-85% | Sweet spot for most teams | | Diminishing Returns | > 90% | Often not worth the effort |

By Code Type

| Code Type | Target Coverage | |-----------|----------------| | Business Logic | 90-95% (critical) | | API Controllers | 80-85% | | Services/Use Cases | 85-90% | | Data Access | 70-80% | | UI Components | 60-70% | | Configuration | 50-60% (often skipped) | | Scripts | 30-50% (manual testing ok) |

By Project Type

Fintech/Healthcare: 90%+ (high stakes) SaaS Products: 80%+ (standard) Internal Tools: 70%+ (adequate) Prototypes/MVPs: 50%+ (minimum viable)

How to Measure

Setup with Common Tools

JavaScript/TypeScript (Jest + Istanbul):

// package.json
{
  "scripts": {
    "test": "jest --coverage",
    "test:watch": "jest --watch --coverage"
  },
  "jest": {
    "collectCoverageFrom": [
      "src/**/*.{js,ts,tsx}",
      "!src/**/*.test.{js,ts,tsx}",
      "!src/**/*.d.ts"
    ],
    "coverageThresholds": {
      "global": {
        "branches": 75,
        "functions": 80,
        "lines": 80,
        "statements": 80
      }
    }
  }
}

Python (pytest + coverage.py):

# .coveragerc
[run]
source = src
omit = */tests/*, */migrations/*

[report]
precision = 2
show_missing = True
skip_covered = False

fail_under = 80

Java (JaCoCo):

<!-- pom.xml -->
<plugin>
  <groupId>org.jacoco</groupId>
  <artifactId>jacoco-maven-plugin</artifactId>
  <configuration>
    <rules>
      <rule>
        <element>BUNDLE</element>
        <limits>
          <limit>
            <counter>LINE</counter>
            <value>COVEREDRATIO</value>
            <minimum>0.80</minimum>
          </limit>
        </limits>
      </rule>
    </rules>
  </configuration>
</plugin>

How to Improve Coverage

1. Set Baseline and Targets

Current Coverage: 62%
6-Month Target: 75%
12-Month Target: 80%

Approach: Add 2-3% per month through:
- Test new code before merging
- Add tests to touched files
- Dedicated testing sprints

2. Enforce Coverage on New Code

# GitHub Actions
- name: Check coverage
  run: |
    npm test -- --coverage
    if [ $(cat coverage/coverage-summary.json | jq '.total.lines.pct') -lt 80 ]; then
      echo "Coverage below 80%"
      exit 1
    fi

3. Block PRs Below Threshold

# codecov.yml
coverage:
  status:
    project:
      default:
        target: 80%
        threshold: 2%  # Allow 2% drop
    patch:
      default:
        target: 90%  # New code must have 90% coverage

4. Focus on High-Value Code

Priority Order:

  1. Business logic (highest value)
  2. Payment/auth flows (critical paths)
  3. API endpoints
  4. Data transformations
  5. UI components
  6. Configuration/setup code (lowest value)

5. Test Pyramid Strategy

        ┌─────────┐
        │ E2E (5%) │
        ├─────────┤
        │Integration│
        │  (15%)   │
        ├─────────┤
        │  Unit    │
        │  (80%)   │
        └─────────┘

Focus coverage efforts on unit tests (fastest, most coverage)

6. Make Testing Easy

Developer Experience:

  • Fast test runs (< 10 seconds for unit tests)
  • Watch mode for instant feedback
  • Clear test utilities and helpers
  • Good examples and templates
  • Parallel test execution

Common Pitfalls

❌ Treating Coverage as a KPI

Problem: Teams write meaningless tests just to hit targets Solution: Review test quality, not just coverage numbers

Bad Test Example:

// Achieves coverage but tests nothing
test('user service', () => {
  const service = new UserService();
  expect(service).toBeDefined();
});

Good Test Example:

test('creates user with hashed password', async () => {
  const user = await userService.create({
    email: 'test@example.com',
    password: 'plaintext'
  });

  expect(user.password).not.toBe('plaintext');
  expect(await bcrypt.compare('plaintext', user.password)).toBe(true);
});

❌ Chasing 100% Coverage

Problem: Diminishing returns, time better spent elsewhere Solution: 80-85% is the sweet spot for most projects

❌ Ignoring Test Quality

Problem: High coverage with brittle, flaky tests Solution: Monitor test reliability and maintainability

❌ Testing Implementation, Not Behavior

Problem: Tests break on refactors, even when behavior unchanged Solution: Test public interfaces and user-visible behavior

❌ Not Excluding Generated Code

Problem: Migrations, generated files lower meaningful coverage Solution: Configure coverage tool to exclude these

Implementation Guide

Week 1: Baseline

# Generate coverage report
npm test -- --coverage

# Review current coverage
# Identify files with 0% coverage
# Document coverage by module

Week 2: Set Standards

# Testing Standards

## Coverage Targets
- Overall: 80%
- Business logic: 90%
- New code (PR): 90%
- Build fails if: < 75%

## What to Test
✓ Business logic
✓ API endpoints
✓ Data transformations
✓ Error handling
✗ Boilerplate/config
✗ Third-party wrappers

Week 3: Automation

# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run tests with coverage
        run: npm test -- --coverage
      - name: Upload to Codecov
        uses: codecov/codecov-action@v3
      - name: Check coverage threshold
        run: |
          if [ $(cat coverage/coverage-summary.json | jq '.total.lines.pct') -lt 80 ]; then
            echo "::error::Coverage below 80%"
            exit 1
          fi

Week 4: Process Integration

  • Add coverage check to PR template
  • Discuss coverage in code reviews
  • Celebrate teams improving coverage
  • Share testing best practices

Dashboard Example

Team View

┌──────────────────────────────────────────────┐
│ Code Coverage: 82.4%                         │
│ ██████████████████████░░░░░░░ Excellent      │
│                                              │
│ Coverage by Type:                            │
│ • Line Coverage:     82.4%                   │
│ • Branch Coverage:   78.1%                   │
│ • Function Coverage: 85.2%                   │
│                                              │
│ Trend: ↑ +3.2% this month                   │
│                                              │
│ Lowest Coverage Files:                       │
│ • legacy/auth.ts        42%  ⚠️             │
│ • services/export.ts    38%  ⚠️             │
│ • utils/formatting.ts   51%                  │
└──────────────────────────────────────────────┘

Module Breakdown

Module            Coverage  Change  Files
─────────────────────────────────────────────
API Controllers   87%       +2%     ✓ Good
Business Logic    92%       +1%     ✓ Excellent
Services          81%       +4%     ✓ Improving
Data Access       76%       -1%     ⚠️ Watch
UI Components     68%       +5%     Improving
Utils             73%       0%      Adequate
─────────────────────────────────────────────
Overall           82.4%     +3.2%   ✓ On Track
  • Change Failure Rate: Does coverage correlate with fewer failures?
  • Defect Escape Rate: Are bugs making it to production?
  • Test Suite Duration: Is testing too slow?
  • Test Flakiness: Are tests reliable?
  • Code Review Time: Do well-tested PRs merge faster?

Tools & Integrations

Coverage Tools

JavaScript/TypeScript:

  • Istanbul (nyc): Industry standard
  • c8: Native V8 coverage
  • Jest: Built-in coverage

Python:

  • coverage.py: Standard Python coverage
  • pytest-cov: pytest integration

Java:

  • JaCoCo: Most popular
  • Cobertura: Alternative
  • IntelliJ IDEA: Built-in

Go:

  • go test -cover: Built-in
  • gocov: Enhanced reporting

C#/.NET:

  • Coverlet: Cross-platform
  • dotCover: JetBrains tool

Coverage Services

  • Codecov: Multi-language support, PR comments
  • Coveralls: GitHub integration
  • SonarQube: Code quality + coverage
  • Code Climate: Quality metrics platform

Questions to Ask

For Teams

  • What's our current coverage baseline?
  • Which modules have the lowest coverage?
  • Are we testing the right things?
  • Are our tests fast and reliable?

For Code Reviews

  • Does this PR include tests?
  • Are edge cases covered?
  • Is the test quality good?
  • Will these tests catch regressions?

For Leadership

  • Is our coverage trending in the right direction?
  • Are we investing enough in test infrastructure?
  • Do teams have time to write tests?
  • Is lack of tests causing production issues?

Success Stories

E-commerce Platform

  • Before: 45% coverage, frequent production bugs
  • After: 85% coverage, 70% reduction in bugs
  • Changes:
    • Enforced 90% coverage on new code
    • Dedicated "testing week" each quarter
    • Added 10% to story estimates for testing
  • Impact: Faster deployments, higher confidence, better sleep

FinTech Startup

  • Before: 62% coverage, afraid to refactor
  • After: 92% coverage, regular refactoring
  • Changes:
    • Made test-writing part of Definition of Done
    • Paired junior engineers with seniors on testing
    • Invested in testing infrastructure
  • Impact: Zero payment processing bugs in 12 months

Advanced Topics

Mutation Testing

Test the tests by introducing bugs:

# Stryker (JavaScript)
npx stryker run

# Mutmut (Python)
mutmut run

Mutation Score: % of injected bugs caught by tests
Target: 80%+ mutation score

Coverage-Guided Fuzzing

Automatically generate test inputs:

# Example with Hypothesis
from hypothesis import given, strategies as st

@given(st.text(), st.integers())
def test_format_message(text, count):
    result = format_message(text, count)
    assert isinstance(result, str)
    assert len(result) > 0

Differential Coverage

Only check coverage on changed code:

# Coverage on PR diff only
diff-cover coverage.xml --compare-branch=main --fail-under=90

Balancing Coverage with Other Goals

Good Balance:
✅ 80% overall coverage
✅ Fast test suite (< 2 min)
✅ Reliable tests (< 1% flaky)
✅ Easy to write new tests
✅ Tests document behavior

Anti-Patterns:
❌ 95% coverage but tests take 30 minutes
❌ 100% coverage but tests are brittle
❌ High coverage of trivial code, low coverage of critical code
❌ Testing implementation details

Conclusion

Code Coverage is a useful quality metric, but it's a means to an end, not the end itself. Aim for 80-85% coverage with a focus on high-value code like business logic and critical paths. Enforce coverage on new code (90%+), make testing easy and fast, and focus on test quality over raw coverage numbers. Remember: 80% coverage with good tests is far better than 95% coverage with meaningless tests. Use coverage to identify gaps, but use judgment to decide what actually needs testing. Start measuring today, set realistic targets, and incrementally improve through enforcement on new code and dedicated improvement efforts.