Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Coverage Integration

Coverage integration is one of Debtmap’s most powerful capabilities, enabling risk-based prioritization by correlating complexity metrics with test coverage. This helps you identify truly risky code—functions that are both complex and untested—rather than just highlighting complex but well-tested functions.

Why Coverage Matters

Without coverage data, complexity analysis shows you what’s complex, but not what’s risky. A complex function with 100% test coverage poses far less risk than a simple function with 0% coverage on a critical path.

Coverage integration transforms Debtmap from a complexity analyzer into a risk assessment tool:

  • Prioritize testing efforts: Focus on high-complexity functions with low coverage
  • Validate refactoring safety: See which complex code is already protected by tests
  • Risk-based sprint planning: Surface truly risky code ahead of well-tested complexity
  • Quantify risk reduction: Measure how coverage improvements reduce project risk

LCOV Format: The Universal Standard

Debtmap uses the LCOV format for coverage data. LCOV is a language-agnostic standard supported by virtually all coverage tools across all major languages.

Why LCOV?

  • Universal compatibility: Works with Rust, Python, JavaScript, TypeScript, Go, and more
  • Tool independence: Not tied to any specific test framework
  • Simple text format: Easy to inspect and debug
  • Widely supported: Generated by most modern coverage tools

LCOV File Structure

An LCOV file contains line-by-line coverage information:

SF:src/analyzer.rs
FN:42,calculate_complexity
FNDA:15,calculate_complexity
DA:42,15
DA:43,15
DA:44,12
DA:45,0
LH:3
LF:4
end_of_record
  • SF: - Source file path
  • FN: - Function name and starting line
  • FNDA: - Function execution count
  • DA: - Line execution data (line number, hit count)
  • LH: - Lines hit
  • LF: - Lines found (total)

Rust Name Demangling

For Rust projects, Debtmap includes sophisticated name demangling to correlate LCOV coverage with analyzed functions. The demangling system handles:

Mangling Schemes:

  • v0 scheme: Starts with _RNv (modern Rust, default since 1.38)
  • Legacy scheme: Starts with _ZN (older Rust versions)

Normalization Process (see src/risk/lcov.rs:demangle_function_name and normalize_demangled_name):

  1. Demangle: Convert mangled symbols to human-readable names
  2. Strip crate hashes: Remove build-specific hash IDs (e.g., debtmap[71f4b4990cdcf1ab]debtmap)
  3. Strip generic parameters: Remove type parameters (e.g., HashMap<K,V>::insertHashMap::insert)
  4. Extract method names: Store both full path and method name for flexible matching

Examples:

Original (in LCOV)After DemanglingNormalized Full PathMethod Name
_RNvXs0_14debtmap...visit_expr<debtmap[hash]::Type>::visit_exprdebtmap::Type::visit_exprvisit_expr
Type::method::<T>Type::method::<T>Type::methodmethod
std::vec::Vec<T>::pushstd::vec::Vec<T>::pushstd::vec::Vec::pushpush

This normalization enables Debtmap to match coverage data even when:

  • Crate hashes change between builds
  • Multiple monomorphizations of generic functions exist
  • LCOV stores simplified names while Debtmap uses qualified names

Generating Coverage Data

Rust: cargo-tarpaulin

Installation:

cargo install cargo-tarpaulin

Generate LCOV:

cargo tarpaulin --out lcov --output-dir target/coverage

Analyze with Debtmap:

debtmap analyze . --lcov target/coverage/lcov.info

Common Issues:

  • Ensure tests compile before running tarpaulin
  • Use --ignore-tests if tests themselves show up in coverage
  • Check paths match your project structure (relative to project root)

JavaScript/TypeScript: Jest

Configuration (package.json or jest.config.js):

{
  "jest": {
    "coverageReporters": ["lcov", "text"]
  }
}

Generate Coverage:

npm test -- --coverage

Analyze with Debtmap:

debtmap analyze . --lcov coverage/lcov.info

Python: pytest-cov

Installation:

pip install pytest-cov

Generate LCOV:

pytest --cov=src --cov-report=lcov

Analyze with Debtmap:

debtmap analyze . --lcov coverage.lcov

Go: go test with gocover-cobertura

Generate Coverage:

go test -coverprofile=coverage.out ./...
gocover-cobertura < coverage.out > coverage.xml
# Convert to LCOV using lcov tools

Note: Go’s native coverage format requires conversion. Most CI systems support LCOV conversion plugins.

Role-Based Coverage Expectations

Not all functions need the same level of test coverage. Debtmap uses a role-based coverage expectation system to adjust scoring based on function purpose (see src/priority/scoring/coverage_expectations.rs and src/risk/evidence/coverage_analyzer.rs).

Function Roles and Coverage Targets

RoleCoverage RangeRationaleRole Weight
PureLogic90-95%Business logic requires comprehensive testing1.0
EntryPoint70-80%Better tested with integration tests0.9
Orchestrator60-80%Coordinates other functions, moderate complexity0.6
IOWrapper40-60%Thin I/O layer, often integration-tested0.4
PatternMatch50-70%Simple pattern matching, lower complexity0.3
Debug20-30%Diagnostic functions, low priority0.2
Unknown70-90%Default for unclassified functions0.8

Source: See src/priority/semantic_classifier/mod.rs:25-32 for role definitions and src/risk/evidence/coverage_analyzer.rs:63-73 for role weight calculation.

Coverage Gap Severity Classification

Debtmap classifies coverage gaps into 4 severity levels (see src/priority/scoring/coverage_expectations.rs:GapSeverity):

SeverityConditionImpact on ScoreVisual
NoneCoverage ≥ targetNo penalty🟢
MinorCoverage between min and targetSmall penalty🟡
ModerateCoverage between 50% of min and minMedium penalty🟠
CriticalCoverage < 50% of minHigh penalty🔴

Example: For PureLogic (target: 95%, min: 90%):

  • 96% coverage → None (🟢)
  • 92% coverage → Minor (🟡)
  • 75% coverage → Moderate (🟠)
  • 40% coverage → Critical (🔴)

Test Quality Classification

Coverage is classified into quality tiers based on both percentage and complexity (see src/risk/evidence/coverage_analyzer.rs:44-52):

#![allow(unused)]
fn main() {
fn classify_test_quality(coverage: f64, complexity: u32) -> TestQuality {
    match () {
        _ if coverage >= 90.0 && complexity <= 5 => TestQuality::Excellent,
        _ if coverage >= 80.0 => TestQuality::Good,
        _ if coverage >= 60.0 => TestQuality::Adequate,
        _ if coverage > 0.0 => TestQuality::Poor,
        _ => TestQuality::Missing,
    }
}
}

Quality Levels:

  • Excellent: ≥90% coverage AND complexity ≤5 (simple, well-tested)
  • Good: ≥80% coverage (comprehensive testing)
  • Adequate: ≥60% coverage (basic testing)
  • Poor: >0% but <60% coverage (incomplete testing)
  • Missing: 0% coverage (no tests)

How Roles Affect Scoring

Role weights adjust the coverage penalty applied to functions (see src/risk/evidence/coverage_analyzer.rs:63-73):

Example: A function with 50% coverage:

  • PureLogic (weight: 1.0): Full penalty, high urgency
  • Orchestrator (weight: 0.6): 60% of full penalty
  • Debug (weight: 0.2): Only 20% of full penalty, low urgency

This ensures that:

  1. Business logic functions are prioritized for testing
  2. Entry points rely more on integration tests
  3. Diagnostic/debug functions don’t create noise

How Coverage Affects Scoring

Coverage data fundamentally changes how Debtmap calculates debt scores. The scoring system operates in two different modes depending on whether coverage data is available.

Scoring Modes

Mode 1: With Coverage Data (Dampening Multiplier)

When you provide an LCOV file with --lcov, coverage acts as a dampening multiplier that reduces scores for well-tested code:

Base Score = (Complexity Factor × 0.50) + (Dependency Factor × 0.25)
Coverage Multiplier = 1.0 - coverage_percentage
Final Score = Base Score × Coverage Multiplier

This is the current implementation as of spec 122. Coverage dampens the base score rather than contributing as an additive component.

Mode 2: Without Coverage Data (Weighted Sum)

When no coverage data is available, Debtmap falls back to a weighted sum model:

Final Score = (Coverage × 0.50) + (Complexity × 0.35) + (Dependency × 0.15)

In this mode, coverage is assumed to be 0% (worst case), giving it a weight of 50% in the total score. See src/priority/scoring/calculation.rs:119-129 for the implementation.

Coverage Dampening Multiplier

When coverage data is provided, it acts as a multiplier that dampens the base score:

Coverage Multiplier = 1.0 - coverage_percentage
Final Score = Base Score × Coverage Multiplier

Examples:

Base ScoreCoverageMultiplierFinal ScorePriority
8.5100%0.00.0Minimal (well-tested)
8.550%0.54.25Medium
8.50%1.08.5High (untested)

Key Insight: Complex but well-tested code automatically drops in priority, while untested complex code rises to the top.

Special Cases:

  • Test functions: Coverage multiplier = 0.0 (tests get near-zero scores regardless of complexity)
  • Entry points: Handled through semantic classification (FunctionRole) system with role multipliers, not coverage-specific weighting

Invariant: Total debt score with coverage ≤ total debt score without coverage.

Implementation: See src/priority/scoring/calculation.rs:68-82 for the coverage dampening calculation.

Transitive Coverage Propagation

Debtmap doesn’t just look at direct coverage—it propagates coverage through the call graph using transitive analysis.

How It Works

A function’s effective coverage considers:

  1. Direct coverage: Lines executed by tests
  2. Caller coverage: Coverage of functions that call this function
Transitive Coverage = Direct Coverage + Σ(Caller Coverage × Weight)

Algorithm Parameters

The transitive coverage propagation uses carefully tuned parameters to balance accuracy and performance:

  • Well-Tested Threshold: 80% - Only functions with ≥80% direct coverage contribute to indirect coverage, ensuring high confidence
  • Distance Discount: 70% per hop - Each level of indirection reduces contribution by 30%, reflecting decreased confidence
  • Maximum Distance: 3 hops - Limits recursion depth to prevent exponential complexity (after 3 hops, contribution drops to ~34%)

These parameters ensure that indirect coverage signals are meaningful while preventing false confidence from distant call relationships. See src/priority/coverage_propagation.rs:38-46 for the implementation.

Why It Matters

A function with 0% direct coverage might have high transitive coverage if it’s only called by well-tested functions:

#![allow(unused)]
fn main() {
// direct_coverage = 0%
// But called only by `process_request` (100% coverage)
// → transitive_coverage = 85%
fn validate_input(data: &str) -> bool {
    data.len() > 0
}

// direct_coverage = 100%
fn process_request(input: String) -> Result<()> {
    if !validate_input(&input) {
        return Err("Invalid");
    }
    // ...
}
}

Effect: validate_input has reduced urgency because it’s only reachable through well-tested code paths.

Generic Function Coverage (Monomorphization)

Challenge: Generic functions in Rust get monomorphized into multiple versions, each appearing as a separate function in LCOV with different coverage.

For example, execute::<T>() might appear as:

  • execute::<WorkflowExecutor> - 70% coverage, uncovered: [10, 20, 30]
  • execute::<MockExecutor> - 80% coverage, uncovered: [20, 40]

Debtmap’s Solution (see src/risk/coverage_index.rs:merge_coverage):

  1. Base Function Index: Maps base names to all monomorphized versions

    • (file, "execute")["execute::<WorkflowExecutor>", "execute::<MockExecutor>"]
  2. Intersection Merge Strategy: A line is uncovered ONLY if ALL versions leave it uncovered

    • Coverage percentage: Average across all versions (75% in example)
    • Uncovered lines: Intersection of uncovered sets ([20] in example)
  3. Conservative Approach: Ensures we don’t claim coverage that doesn’t exist in all code paths

Example Aggregation:

VersionCoverageUncovered Lines
execute::<WorkflowExecutor>70%[10, 20, 30]
execute::<MockExecutor>80%[20, 40]
Aggregated Result75%[20]

Line 20 is uncovered in BOTH versions, so it’s risky. Lines 10, 30, 40 are covered in at least one version, so they’re considered safer.

Implementation: See src/risk/coverage_index.rs:AggregateCoverage and merge_coverage (lines 50-139).

Trait Method Coverage Matching

Challenge: LCOV files may store trait method implementations with simplified names while Debtmap tracks fully qualified names.

Example Mismatch:

  • LCOV stores: visit_expr (demangled method name)
  • Debtmap queries: RecursiveMatchDetector::visit_expr (fully qualified)

Debtmap’s Solution (see src/risk/coverage_index.rs:generate_name_variants and method_name_index):

  1. Name Variant Generation: Extract method name from qualified paths

    • RecursiveMatchDetector::visit_expr → generates variant visit_expr
  2. Method Name Index: O(1) lookup from method name to all implementations

    • (file, "visit_expr")["RecursiveMatchDetector::visit_expr", "_RNvXs0_...visit_expr"]
  3. Multi-Strategy Matching: Try variants if exact match fails

    • First: Exact qualified name match
    • Second: Method name variant match
    • Third: Line-based fallback

Implementation: See src/risk/coverage_index.rs:192 (method_name_index) and generate_name_variants (lines 12-48).

Performance Characteristics

Coverage integration is highly optimized for large codebases using a multi-strategy lookup system.

Coverage Index Structure

The coverage index uses nested HashMaps plus supporting indexes for O(1) lookups (see src/risk/coverage_index.rs:172-196):

  1. by_file: HashMap<PathBuf, HashMap<String, FunctionCoverage>> - Primary nested index
  2. by_line: HashMap<PathBuf, BTreeMap<usize, FunctionCoverage>> - Line-based range queries
  3. base_function_index: Maps base names to monomorphized versions (generic handling)
  4. method_name_index: Maps method names to full qualified names (trait methods)

Lookup Strategy Waterfall

Debtmap tries 5 strategies in order, stopping at the first match (see src/risk/coverage_index.rs:get_function_coverage_with_line):

StrategyComplexityWhen It MatchesTypical Latency
1. Exact MatchO(1)File path and function name exactly match LCOV~0.5μs
2. Suffix MatchingO(files)Query path ends with LCOV file path, then O(1) function lookup~5-8μs
3. Reverse SuffixO(files)LCOV file path ends with query path, then O(1) function lookup~5-8μs
4. Normalized EqualityO(files)Paths equal after normalizing ./ prefix, then O(1) function lookup~5-8μs
5. Line-Based FallbackO(log n)Match by line number ±2 tolerance using BTreeMap range query~10-15μs

Strategy Optimizations:

  • Path strategies iterate over FILES (typically ~375) not functions (~1,500+), providing 4x-50x speedup
  • Each path strategy tries 3 name matching techniques per file:
    1. Exact name match
    2. Function name suffix match (handles qualified vs short names)
    3. Method name match (handles trait implementations)

Performance Benchmarks

  • Index Build: O(n), ~20-30ms for 5,000 functions
  • Exact Lookup: O(1), ~0.5μs per lookup
  • Path Strategy Fallback: O(files) × O(1), ~5-8μs when exact match fails
  • Line-Based Fallback: O(log n), ~10-15μs when all path strategies fail
  • Memory Usage: ~200 bytes per record (~2MB for 5,000 functions)
  • Thread Safety: Lock-free parallel access via Arc<CoverageIndex>
  • Analysis Overhead: ~2.5x baseline (target: ≤3x)

Result: Coverage integration adds minimal overhead even on projects with thousands of functions.

Debugging Lookup Performance

The coverage index tracks detailed statistics for performance analysis (see src/risk/coverage_index.rs:CoverageIndexStats):

#![allow(unused)]
fn main() {
pub struct CoverageIndexStats {
    pub total_files: usize,
    pub total_records: usize,
    pub index_build_time: Duration,
    pub estimated_memory_bytes: usize,
}
}

Enable verbose logging to see which strategy matched:

debtmap analyze . --lcov coverage.info -vv

Output shows strategy attempts:

Looking up coverage for function 'visit_expr' in file 'src/detector.rs'
Strategy 1: Suffix matching...
  Found path match: 'src/detector.rs'
  ✓ Matched method name 'visit_expr' -> 'RecursiveMatchDetector::visit_expr': 85%

CLI Options Reference

Primary Coverage Options

# Provide LCOV coverage file
debtmap analyze . --coverage-file path/to/lcov.info

# Shorthand alias
debtmap analyze . --lcov path/to/lcov.info

Context Providers

Coverage can be combined with other context providers for nuanced risk assessment:

# Enable all context providers (includes coverage propagation)
debtmap analyze . --lcov coverage.info --enable-context

# Specify specific providers
debtmap analyze . --lcov coverage.info \
  --context-providers critical_path,dependency,git_history

# Disable specific providers
debtmap analyze . --lcov coverage.info \
  --disable-context git_history

Available Context Providers:

  • critical_path: Identifies functions on critical execution paths
  • dependency: Analyzes dependency relationships and impact
  • git_history: Uses change frequency from version control

See Scoring Strategies for details on how these combine.

Validate Command Support

The validate command also supports coverage integration for risk-based quality gates:

# Fail CI builds if untested complex code exceeds thresholds
debtmap validate . --lcov coverage.info --max-debt-density 50

See CLI Reference for complete validation options.

Troubleshooting Coverage Integration

Coverage Not Correlating with Functions

Symptoms:

  • Debtmap shows 0% coverage for all functions
  • Warning: “No coverage data correlated with analyzed functions”

Solutions:

  1. Verify LCOV Format:
head coverage.info
# Should show: SF:, FN:, DA: lines
  1. Check Path Matching: Coverage file paths must be relative to project root:
# Good: SF:src/analyzer.rs
# Bad:  SF:/home/user/project/src/analyzer.rs
  1. Use explain-coverage Command:
debtmap explain-coverage . --lcov coverage.info \
  --function validate_input \
  --file src/validator.rs \
  --format json

The explain-coverage command provides detailed diagnostics:

JSON Output Structure (see src/commands/explain_coverage.rs:ExplainCoverageResult):

{
  "function_name": "validate_input",
  "file_path": "src/validator.rs",
  "coverage_found": true,
  "coverage_percentage": 85.0,
  "matched_by_strategy": "Suffix Matching",
  "attempts": [
    {
      "strategy": "Exact Match",
      "success": false,
      "matched_function": null,
      "coverage_percentage": null
    },
    {
      "strategy": "Suffix Matching",
      "success": true,
      "matched_function": "validator::validate_input",
      "matched_file": "src/validator.rs",
      "coverage_percentage": 85.0
    }
  ],
  "available_functions": [
    "src/validator.rs::validate_input",
    "src/processor.rs::process_request"
  ],
  "available_files": [
    "src/validator.rs",
    "src/processor.rs"
  ]
}

Key Fields:

  • attempts[]: Shows all 5 strategies tried and which succeeded
  • available_functions[]: All functions found in LCOV (helps identify naming mismatches)
  • available_files[]: All files in LCOV (helps debug path issues)
  1. Enable Verbose Logging:
debtmap analyze . --lcov coverage.info -vv

This shows coverage lookup details for each function during analysis.

  1. Verify Coverage Tool Output:
# Ensure your coverage tool generated line data (DA: records)
grep "^DA:" coverage.info | head

Functions Still Show Up Despite 100% Coverage

This is expected behavior when:

  • Function has high complexity (cyclomatic > 10)
  • Function has other debt issues (duplication, nesting, etc.)
  • You’re viewing function-level output (coverage dampens but doesn’t eliminate)

Coverage reduces priority but doesn’t hide issues. Use filters to focus:

# Show only critical and high priority items
debtmap analyze . --lcov coverage.info --min-priority high

# Show top 10 most urgent items
debtmap analyze . --lcov coverage.info --top 10

Coverage File Path Issues

Problem: Can’t find coverage file

Solutions:

# Use absolute path
debtmap analyze . --lcov /absolute/path/to/coverage.info

# Or ensure relative path is from project root
debtmap analyze . --lcov ./target/coverage/lcov.info

LCOV Format Errors

Problem: “Invalid LCOV format” error

Causes:

  • Non-LCOV format (Cobertura XML, JaCoCo, etc.)
  • Corrupted file
  • Wrong file encoding

Solutions:

  • Verify your coverage tool is configured for LCOV output
  • Check for binary/encoding issues: file coverage.info
  • Regenerate coverage with explicit LCOV format flag

See Troubleshooting for more debugging tips.

Best Practices

Analysis Workflow

  1. Generate Coverage Before Analysis:

    # Rust example
    cargo tarpaulin --out lcov --output-dir target/coverage
    debtmap analyze . --lcov target/coverage/lcov.info
    
  2. Use Coverage for Sprint Planning:

    # Focus on untested complex code
    debtmap analyze . --lcov coverage.info --top 20
    
  3. Combine with Tiered Prioritization: Coverage automatically feeds into Tiered Prioritization:

    • Tier 1: Architectural issues (less affected by coverage)
    • Tier 2: Complex untested code (coverage < 50%, complexity > 15)
    • Tier 3: Testing gaps (coverage < 80%, complexity 10-15)
  4. Validate Refactoring Impact:

    # Before refactoring
    debtmap analyze . --lcov coverage-before.info -o before.json
    
    # After refactoring
    debtmap analyze . --lcov coverage-after.info -o after.json
    
    # Compare
    debtmap compare --before before.json --after after.json
    

Testing Strategy

Prioritize testing based on risk:

  1. High Complexity + Low Coverage = Highest Priority:

    debtmap analyze . --lcov coverage.info \
      --filter Risk --min-priority high
    
  2. Focus on Business Logic: Entry points and infrastructure code have natural coverage patterns. Focus unit tests on business logic functions.

  3. Use Dependency Analysis:

    debtmap analyze . --lcov coverage.info \
      --context-providers dependency -vv
    

    Tests high-dependency functions first—they have the most impact.

  4. Don’t Over-Test Entry Points: Entry points (main, handlers) are better tested with integration tests, not unit tests. Debtmap applies role multipliers through its semantic classification system (FunctionRole) to adjust scoring for different function types. See src/priority/unified_scorer.rs:149 and src/priority/scoring/classification.rs for the classification system.

Configuration

In .debtmap.toml:

[scoring]
# Default weights for scoring WITHOUT coverage data
# When coverage data IS provided, it acts as a dampening multiplier instead
coverage = 0.50  # Default: 50% (only used when no LCOV provided)
complexity = 0.35  # Default: 35%
dependency = 0.15  # Default: 15%

[thresholds]
# Set minimum risk score to filter low-priority items
minimum_risk_score = 15.0

# Skip simple functions even if uncovered
minimum_cyclomatic_complexity = 5

Important: These weights are from the deprecated additive scoring model. The current implementation (spec 122) calculates a base score from complexity (50%) and dependency (25%) factors, then applies coverage as a dampening multiplier: Final Score = Base Score × (1.0 - coverage_pct). These weights only apply when coverage data is not available. See src/priority/scoring/calculation.rs:68-82 for the coverage dampening calculation and src/priority/scoring/calculation.rs:119-129 for the fallback weighted sum mode.

See Configuration for complete options.

CI Integration

Example GitHub Actions Workflow:

- name: Generate Coverage
  run: cargo tarpaulin --out lcov --output-dir target/coverage

- name: Analyze with Debtmap
  run: |
    debtmap analyze . \
      --lcov target/coverage/lcov.info \
      --format json \
      --output debtmap-report.json

- name: Validate Quality Gates
  run: |
    debtmap validate . \
      --lcov target/coverage/lcov.info \
      --max-debt-density 50

Quality Gate Strategy:

  • Fail builds on new critical debt (Tier 1 architectural issues)
  • Warn on new high-priority untested code (Tier 2)
  • Track coverage trends over time with compare command

Complete Language Examples

Rust End-to-End

# 1. Generate coverage
cargo tarpaulin --out lcov --output-dir target/coverage

# 2. Verify LCOV output
head target/coverage/lcov.info

# 3. Run Debtmap with coverage
debtmap analyze . --lcov target/coverage/lcov.info

# 4. Interpret results (look for [UNTESTED] markers on high-complexity functions)

JavaScript/TypeScript End-to-End

# 1. Configure Jest for LCOV (in package.json or jest.config.js)
# "coverageReporters": ["lcov", "text"]

# 2. Generate coverage
npm test -- --coverage

# 3. Verify LCOV output
head coverage/lcov.info

# 4. Run Debtmap
debtmap analyze . --lcov coverage/lcov.info --languages javascript,typescript

Python End-to-End

# 1. Install pytest-cov
pip install pytest-cov

# 2. Generate LCOV coverage
pytest --cov=src --cov-report=lcov

# 3. Verify output
head coverage.lcov

# 4. Run Debtmap
debtmap analyze . --lcov coverage.lcov --languages python

Go End-to-End

# 1. Generate native coverage
go test -coverprofile=coverage.out ./...

# 2. Convert to LCOV (requires gocover-cobertura or similar)
# Note: This step is tool-dependent

# 3. Run Debtmap
debtmap analyze . --lcov coverage.lcov --languages go

FAQ

Why does my 100% covered function still show up?

Coverage dampens debt scores but doesn’t eliminate debt. A function with cyclomatic complexity 25 and 100% coverage still represents technical debt—it’s just lower priority than untested complex code.

Use filters to focus on high-priority items:

debtmap analyze . --lcov coverage.info --top 10

What’s the difference between direct and transitive coverage?

  • Direct coverage: Lines executed directly by tests
  • Transitive coverage: Coverage considering call graph (functions called by well-tested code)

Transitive coverage reduces urgency for functions only reachable through well-tested paths.

Should I test everything to 100% coverage?

No. Use Debtmap’s risk scores to prioritize:

  1. Test high-complexity, low-coverage functions first
  2. Entry points are better tested with integration tests
  3. Simple utility functions (complexity < 5) may not need dedicated unit tests

Debtmap helps you achieve optimal coverage, not maximal coverage.

How do I debug coverage correlation issues?

Use verbose logging:

debtmap analyze . --lcov coverage.info -vv

This shows:

  • Coverage file parsing details
  • Function-to-coverage correlation attempts
  • Path matching diagnostics

Can I use coverage with validate command?

Yes! The validate command supports --lcov for risk-based quality gates:

debtmap validate . --lcov coverage.info --max-debt-density 50

See CLI Reference for details.

Further Reading