debtmap 0.16.4

Code complexity and technical debt analyzer
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
# Architecture

This chapter explains how debtmap's analysis pipeline works, from discovering files to producing prioritized technical debt signals.

## Analysis Pipeline Overview

Debtmap's analysis follows a multi-stage pipeline that transforms source code into structured signals:

```
┌─────────────────┐
│ File Discovery  │
└────────┬────────┘
┌─────────────────┐
│Language Detection│
└────────┬────────┘
    ┌────────┐
    │ Parser │
    └────┬───┘
    ┌────┼────────────┐
    │    │            │
    ▼    ▼            ▼
┌─────┐ ┌──────────┐ ┌───────────┐
│ syn │ │rustpython│ │tree-sitter│
│ AST │ │   AST    │ │    AST    │
└──┬──┘ └────┬─────┘ └─────┬─────┘
   │         │             │
   └─────────┼─────────────┘
  ┌──────────────────┐
  │ Metric Extraction │
  └─────────┬────────┘
    ┌───────┼───────┐
    │       │       │
    ▼       ▼       ▼
┌────────┐ ┌─────┐ ┌─────────┐
│Complexity│ │Call │ │ Pattern │
│  Calc   │ │Graph│ │Detection│
└────┬───┘ └──┬──┘ └────┬────┘
     │        │         │
     ▼        │         │
┌─────────┐   │         │
│ Entropy │   │         │
│ Analysis│   │         │
└────┬────┘   │         │
     │        │         │
     ▼        ▼         ▼
┌─────────┐ ┌────────┐ ┌──────┐    ┌──────────┐
│Effective│ │Dependency│ │ Debt │    │   LCOV   │
│Complexity│ │Analysis│ │Class │    │ Coverage │
└────┬────┘ └────┬───┘ └──┬───┘    └────┬─────┘
     │           │        │             │
     └───────────┼────────┼─────────────┘
                 │        │
                 ▼        ▼
           ┌─────────────────┐
           │  Risk Scoring   │
           └────────┬────────┘
         ┌───────────────────┐
         │Tiered Prioritization│
         └─────────┬─────────┘
        ┌──────────────────────┐
        │ Context Suggestion   │
        │    Generation        │
        └─────────┬────────────┘
          ┌────────────────┐
          │Output Formatting│
          └────────┬───────┘
       ┌───────────┼───────────┐
       │           │           │
       ▼           ▼           ▼
   ┌──────┐   ┌────────┐   ┌─────┐
   │ JSON │   │LLM-MD  │   │Term │
   └──────┘   └────────┘   └─────┘
```

## Key Components

### 1. File Discovery and Language Detection

**Purpose:** Identify source files to analyze and determine their language.

**How it works:**
- Walks the project directory tree (respecting `.gitignore` and `.debtmapignore`)
- Detects language based on file extension (`.rs`, `.py`, `.js`, `.ts`)
- Filters out test files, build artifacts, and vendored dependencies
- Groups files by language for parallel processing

**Configuration:**
```toml
[analysis]
exclude_patterns = ["**/tests/**", "**/target/**", "**/node_modules/**"]
include_patterns = ["src/**/*.rs", "lib/**/*.py"]
```

### 2. Parser Layer

**Purpose:** Convert source code into Abstract Syntax Trees (ASTs) for analysis.

**Language-Specific Parsers:**

**Rust (syn):**
- Uses the `syn` crate for full Rust syntax support
- Extracts: functions, structs, impls, traits, macros
- Handles: async/await, generic types, lifetime annotations
- Performance: ~10-20ms per file

**Python (rustpython):**
- Uses rustpython's parser for Python 3.x syntax
- Extracts: functions, classes, methods, decorators
- Handles: comprehensions, async/await, type hints
- Performance: ~5-15ms per file

**JavaScript/TypeScript (tree-sitter):**
- Uses tree-sitter for JS/TS parsing
- Extracts: functions, classes, arrow functions, hooks
- Handles: JSX/TSX, decorators, generics
- Performance: ~8-18ms per file

**Error Handling:**
- Syntax errors logged but don't stop analysis
- Partial ASTs used when possible
- Files with parse errors excluded from final report

### 3. Metric Extraction

**Purpose:** Extract raw metrics from ASTs.

**Metrics Computed:**

**Function-Level:**
- Lines of code (LOC)
- Cyclomatic complexity (branch count)
- Nesting depth (max indentation level)
- Parameter count
- Return path count
- Comment ratio

**File-Level:**
- Total LOC
- Number of functions/classes
- Dependency count (imports)
- Documentation coverage

**Implementation:**
```rust
pub struct FunctionMetrics {
    pub name: String,
    pub location: Location,
    pub loc: u32,
    pub cyclomatic_complexity: u32,
    pub nesting_depth: u32,
    pub parameter_count: u32,
    pub return_paths: u32,
}
```

### 4. Complexity Calculation and Entropy Analysis

**Purpose:** Compute effective complexity using entropy-adjusted metrics.

**Traditional Cyclomatic Complexity:**
- Count decision points (if, match, loop, etc.)
- Each branch adds +1 to complexity
- Does not distinguish between repetitive and varied logic

**Entropy-Based Adjustment:**

Debtmap calculates pattern entropy to adjust cyclomatic complexity:

1. **Extract patterns** - Identify branch structures (e.g., all if/return patterns)
2. **Calculate variety** - Measure information entropy of patterns
3. **Adjust complexity** - Reduce score for low-entropy (repetitive) code

**Formula:**
```
Entropy = -Σ(p_i * log2(p_i))

where p_i = frequency of pattern i

Effective Complexity = Cyclomatic * (1 - (1 - Entropy/Max_Entropy) * 0.75)
```

**Example:**
```rust
// 20 similar if/return statements
// Cyclomatic: 20, Entropy: 0.3
// Effective: 20 * (1 - (1 - 0.3/4.32) * 0.75) ≈ 5.5
```

This approach reduces false positives from validation/configuration code while still flagging genuinely complex logic.

### 5. Call Graph Construction

**Purpose:** Understand function dependencies and identify critical paths.

**What's Tracked:**
- Function calls within the same file
- Cross-file calls (when possible to resolve)
- Method calls on structs/classes
- Trait/interface implementations

**Analysis:**
- **Fan-in:** How many functions call this function
- **Fan-out:** How many functions this function calls
- **Depth:** Distance from entry points (main, handlers)
- **Cycles:** Detect recursive calls

**Usage:**
- Prioritize functions called from many untested paths
- Identify central functions (high fan-in/fan-out)
- Detect test coverage gaps in critical paths

**Limitations:**
- Dynamic dispatch not fully resolved
- Cross-crate calls require additional analysis
- Closures and function pointers approximated

### 6. Pattern Detection and Debt Classification

**Purpose:** Identify specific technical debt patterns.

**Debt Categories:**

**Test Gaps:**
- Functions with 0% coverage and high complexity
- Untested error paths
- Missing edge case tests

**Complexity Issues:**
- Functions exceeding thresholds (default: 10)
- Deep nesting (3+ levels)
- Long functions (200+ LOC)

**Design Smells:**
- God functions (high fan-out)
- Unused code (fan-in = 0)
- Circular dependencies

**Implementation:**
```rust
pub enum DebtType {
    TestingGap { coverage: f64, cyclomatic: u32, cognitive: u32 },
    ComplexityHotspot { cyclomatic: u32, cognitive: u32 },
    DeadCode { visibility: FunctionVisibility, cyclomatic: u32, cognitive: u32, usage_hints: Vec<String> },
    GodObject { methods: u32, fields: Option<u32>, responsibilities: u32, god_object_score: f64, lines: u32 },
    // ... 35 total variants
}
```

### 7. Coverage Integration

**Purpose:** Map test coverage data to complexity metrics for risk scoring.

**Coverage Data Flow:**

1. **Read LCOV file** - Parse coverage report from test runners
2. **Map to source** - Match coverage lines to functions/branches
3. **Calculate coverage %** - For each function, compute:
   - Line coverage: % of lines executed
   - Branch coverage: % of branches taken
4. **Identify gaps** - Find untested branches in complex functions

**Coverage Scoring:**
```rust
pub struct CoverageMetrics {
    pub lines_covered: u32,
    pub lines_total: u32,
    pub branches_covered: u32,
    pub branches_total: u32,
    pub coverage_percent: f64,
}
```

**Special Cases:**
- Entry points (main, handlers) expect integration test coverage
- Generated code excluded from coverage requirements
- Test files themselves not analyzed for coverage

### 8. Risk Scoring

**Purpose:** Combine complexity and coverage into a unified risk score.

**Risk Formula:**
```
Risk Score = (Effective Complexity * Coverage Gap Weight) + (Call Graph Depth * Path Weight)

where:
- Effective Complexity: Entropy-adjusted cyclomatic complexity
- Coverage Gap Weight: 1.0 for 0% coverage, decreasing to 0.1 for 95%+
- Call Graph Depth: Distance from entry points
- Path Weight: Number of untested paths leading to this function
```

**Example Calculation:**
```rust
fn calculate_risk_score():
  Effective Complexity: 8.5
  Coverage: 30%
  Coverage Gap Weight: 0.7
  Call Graph Depth: 3
  Untested Paths: 2

  Risk = (8.5 * 0.7) + (3 * 2 * 0.3) = 5.95 + 1.8 = 7.75
```

**Risk Tiers (Unified Score 0-10):**
- **Critical (9.0-10.0):** Severe risk requiring immediate attention
- **High (7.0-8.9):** Significant risk, address this sprint
- **Medium (5.0-6.9):** Moderate risk, plan for next sprint
- **Low (3.0-4.9):** Minor risk, monitor
- **Minimal (0.0-2.9):** Well-managed code

### 9. Tiered Prioritization

**Purpose:** Classify and rank technical debt items by severity.

**Prioritization Algorithm:**

1. **Calculate base risk score** (from Risk Scoring step)
2. **Apply context adjustments:**
   - Entry points: -2.0 score (integration test coverage expected)
   - Core business logic: +1.5 score (higher priority)
   - Frequently changed files: +1.0 score (git history analysis)
   - Critical paths: +0.5 score per untested caller
3. **Classify into tiers:**
   - Critical: score >= 9.0
   - High: score >= 7.0
   - Medium: score >= 5.0
   - Low: score >= 3.0
   - Minimal: score < 3.0
4. **Sort within tiers by:**
   - Severity score
   - Coupling impact
   - File location (group related items)

**Output:**
```rust
pub struct PrioritizedDebtItem {
    pub rank: u32,
    pub score: f64,
    pub tier: Tier,
    pub location: Location,
    pub debt_type: DebtType,
    pub metrics: ComplexityMetrics,
    pub coverage: Option<CoverageMetrics>,
    pub context: ContextSuggestion,
}
```

See [Tiered Prioritization](tiered-prioritization.md) for detailed explanation of the ranking algorithm.

### 10. Context Suggestion Generation

**Purpose:** Provide AI agents with specific file ranges to read for understanding the debt item.

**Context Types:**

**Primary Context:**
- The function/struct where debt is located
- Start and end line numbers
- File path

**Related Context:**
- **Callers:** Functions that call this function
- **Callees:** Functions this function calls
- **Tests:** Existing test files that cover related code
- **Types:** Struct/enum definitions used by this function

**Selection Algorithm:**
1. Include primary location (always)
2. Add top 3 callers by call frequency
3. Add callees that are untested
4. Add test files with matching function names
5. Limit total context to ~500 lines (configurable)

**Output Format:**
```
CONTEXT:
├─ Primary: src/parser.rs:38-85
├─ Caller: src/handler.rs:100-120 (calls 12x)
├─ Caller: src/api.rs:45-60 (calls 8x)
├─ Callee: src/tokenizer.rs:15-40 (untested)
└─ Test: tests/parser_test.rs:50-120
```

### 11. Output Formatting

**Purpose:** Present analysis results in formats optimized for different consumers.

**Output Formats:**

**Markdown (--format markdown):**
- Structured for minimal token usage
- Context suggestions included inline
- Metrics in consistent tabular format
- Designed for piping to AI assistants

**JSON (--format json):**
- Machine-readable for CI/CD integration
- Full metadata for each debt item
- Stable schema for programmatic consumption
- Schema-versioned for compatibility

**Terminal (--format terminal):**
- Color-coded by tier (red=critical, yellow=high, etc.)
- Hierarchical tree view with unicode box characters
- Progress bars for analysis phases
- Summary statistics at top

**Markdown (--format markdown):**
- Rendered in GitHub/GitLab for PR comments
- Embedded code blocks with syntax highlighting
- Collapsible details sections
- Linked to source code locations

See [Output Formats](output-formats.md) for examples and configuration options.

## Data Flow Example

Let's trace a single function through the entire pipeline:

**Input: Source File**
```rust
// src/handlers.rs
pub fn process_request(req: Request) -> Result<Response> {
    validate_auth(&req)?;
    let data = parse_payload(&req.body)?;
    let result = apply_business_logic(data)?;
    format_response(result)
}
```

**Stage 1: Parsing**
```rust
FunctionAst {
    name: "process_request",
    location: Location { file: "src/handlers.rs", line: 2 },
    calls: ["validate_auth", "parse_payload", "apply_business_logic", "format_response"],
    ...
}
```

**Stage 2: Metric Extraction**
```rust
FunctionMetrics {
    name: "process_request",
    cyclomatic_complexity: 4,  // 3 ?-operators + base
    nesting_depth: 1,
    loc: 5,
    ...
}
```

**Stage 3: Entropy Analysis**
```rust
// Pattern: repetitive ?-operator error handling
Entropy: 0.4 (low variety)
Effective Complexity: 4 * 0.85 = 3.4
```

**Stage 4: Call Graph**
```rust
CallGraphNode {
    function: "process_request",
    fan_in: 3,  // called from 3 handlers
    fan_out: 4,  // calls 4 functions
    depth: 1,  // direct handler (entry point)
}
```

**Stage 5: Coverage (from LCOV)**
```rust
CoverageMetrics {
    lines_covered: 5,
    lines_total: 5,
    branches_covered: 3,
    branches_total: 4,  // Missing one error path
    coverage_percent: 75%,
}
```

**Stage 6: Risk Scoring**
```rust
Risk = (3.4 * 0.25) + (1 * 1 * 0.2) = 0.85 + 0.2 = 1.05
Tier: LOW (entry point with decent coverage)
```

**Stage 7: Context Suggestion**
```
CONTEXT:
├─ Primary: src/handlers.rs:2-6
├─ Callee: src/auth.rs:15-30 (validate_auth)
└─ Test: tests/integration/handlers_test.rs:10-25
```

**Stage 8: Output**
```
#23 SCORE: 1.1 [LOW]
├─ src/handlers.rs:2 process_request()
├─ COMPLEXITY: cyclomatic=4, cognitive=3, nesting=1
├─ COVERAGE: 75% (1 branch untested)
└─ CONTEXT: Primary + 1 callee + 1 test file
```

## Performance Characteristics

**Analysis Speed:**
- Small project (< 10k LOC): 1-3 seconds
- Medium project (10-50k LOC): 5-15 seconds
- Large project (50-200k LOC): 20-60 seconds
- Very large project (200k+ LOC): 1-5 minutes

**Parallelization:**
- File parsing: Parallel across all available cores
- Metric extraction: Parallel per-file
- Call graph construction: Parallel with work stealing
- Risk scoring: Parallel per-function
- Output formatting: Sequential

**Memory Usage:**
- Approx 100-200 KB per file analyzed
- Peak memory for large projects: 500 MB - 1 GB
- Streaming mode available for very large codebases

**Optimization Strategies:**
- Skip unchanged files (git diff integration)
- Parallel processing with rayon
- Efficient AST traversal (visitor pattern)
- Memory-efficient streaming for large codebases

## Extension Points

**Custom Analyzers:**
Implement the `Analyzer` trait to add language support:

```rust
pub trait Analyzer {
    fn parse(&self, content: &str) -> Result<Ast>;
    fn extract_metrics(&self, ast: &Ast) -> Vec<FunctionMetrics>;
    fn detect_patterns(&self, ast: &Ast) -> Vec<DebtPattern>;
}
```

**Custom Scoring:**
Implement the `RiskScorer` trait to adjust scoring logic:

```rust
pub trait RiskScorer {
    fn calculate_risk(&self, metrics: &FunctionMetrics, coverage: &CoverageMetrics) -> f64;
    fn classify_tier(&self, score: f64) -> Tier;
}
```

**Custom Output:**
Implement the `OutputFormatter` trait for new formats:

```rust
pub trait OutputFormatter {
    fn format(&self, items: &[PrioritizedDebtItem]) -> Result<String>;
}
```

## Next Steps

- **Understand prioritization:** See [Tiered Prioritization]tiered-prioritization.md
- **Learn scoring strategies:** See [Scoring Strategies]scoring-strategies.md
- **Configure analysis:** See [Configuration]configuration.md
- **Integrate with AI:** See [LLM Integration]llm-integration.md