# Debt Patterns
Debtmap detects **35 types of technical debt**, organized into 4 strategic categories. Each debt type is mapped to a category that guides prioritization and remediation strategies.
The 35 debt types consist of:
- **26 priority-specific variants**: Specialized debt patterns with detailed diagnostic data
- **9 legacy variants**: Backward-compatible types from earlier versions (prefer specialized variants)
**Source**: DebtType enum defined in `src/priority/debt_types.rs:47-205`, category mappings in `src/priority/mod.rs:216-254`
## Debt Type Enum
The `DebtType` enum defines all specific debt patterns that Debtmap can detect.
**Note**: Each `DebtType` variant carries structured diagnostic data specific to that pattern. For example, `ComplexityHotspot` includes `cyclomatic` and `cognitive` fields that provide detailed metrics for the detected issue. This structured data enables precise prioritization.
**Source**: DebtType structure defined in `src/priority/debt_types.rs:47-205`
### Priority-Specific Debt Types (26 types)
These are the primary debt types with specialized detection logic and detailed diagnostic data.
**Testing Debt (6 types):**
- `TestingGap` - Functions with insufficient test coverage (fields: `coverage`, `cyclomatic`, `cognitive`)
- `TestTodo` - TODO comments in test code (fields: `priority`, `reason`)
- `TestDuplication` - Duplicated code in test files (fields: `instances`, `total_lines`, `similarity`)
- `TestComplexityHotspot` - Complex test logic that's hard to maintain (fields: `cyclomatic`, `cognitive`, `threshold`)
- `AssertionComplexity` - Complex test assertions (fields: `assertion_count`, `complexity_score`)
- `FlakyTestPattern` - Non-deterministic test behavior (fields: `pattern_type`, `reliability_impact`)
**Architecture Debt (6 types):**
- `GodObject` - Unified variant for classes/files/modules with too many responsibilities (fields: `methods`, `fields`, `responsibilities`, `god_object_score`, `lines`). The detection type (GodClass, GodFile, GodModule) is determined by `god_object_indicators.detection_type` in the parent structure.
- `FeatureEnvy` - Using more data from other objects than own (fields: `external_class`, `usage_ratio`)
- `PrimitiveObsession` - Overusing basic types instead of domain objects (fields: `primitive_type`, `domain_concept`)
- `ScatteredType` - Types with methods scattered across multiple files (fields: `type_name`, `total_methods`, `file_count`, `severity` as String)
- `OrphanedFunctions` - Functions that should be methods on a type (fields: `target_type`, `function_count`, `file_count`)
- `UtilitiesSprawl` - Utility modules with poor cohesion (fields: `function_count`, `distinct_types`)
**Performance Debt (8 types):**
- `AllocationInefficiency` - Inefficient memory allocations (fields: `pattern`, `impact`)
- `StringConcatenation` - Inefficient string building in loops (fields: `loop_type`, `iterations`)
- `NestedLoops` - Multiple nested iterations O(n²) or worse (fields: `depth`, `complexity_estimate`)
- `BlockingIO` - Blocking I/O in async contexts (fields: `operation`, `context`)
- `SuboptimalDataStructure` - Wrong data structure for access pattern (fields: `current_type`, `recommended_type`)
- `AsyncMisuse` - Improper async/await usage (fields: `pattern`, `performance_impact`)
- `ResourceLeak` - Resources not properly released (fields: `resource_type`, `cleanup_missing`)
- `CollectionInefficiency` - Inefficient collection operations (fields: `collection_type`, `inefficiency_type`)
**Code Quality Debt (6 types):**
- `ComplexityHotspot` - Functions exceeding complexity thresholds (fields: `cyclomatic`, `cognitive`)
- `DeadCode` - Unreachable or unused code (fields: `visibility`, `cyclomatic`, `cognitive`, `usage_hints`)
- `MagicValues` - Unexplained literal values (fields: `value`, `occurrences`)
- `Risk` - High-risk code combining complexity + poor test coverage (fields: `risk_score`, `factors`)
- `Duplication` - Duplicated code blocks (fields: `instances`, `total_lines`)
- `ErrorSwallowing` - Errors caught but ignored (fields: `pattern`, `context`)
### Legacy Debt Types (9 types)
These variants are maintained for backward compatibility. For new analysis, prefer the specialized priority-specific variants listed above.
**Source**: Legacy variants defined in `src/priority/debt_types.rs:48-77`
| `Todo` | Use for general TODO markers | Fields: `reason` |
| `Fixme` | Use for general FIXME markers | Fields: `reason` |
| `CodeSmell` | Use specific debt types | Fields: `smell_type` |
| `Complexity` | `ComplexityHotspot` | Fields: `cyclomatic`, `cognitive` |
| `Dependency` | Specific architecture types | Fields: `dependency_type` |
| `ResourceManagement` | `ResourceLeak`, `AllocationInefficiency` | Fields: `issue_type` |
| `CodeOrganization` | `ScatteredType`, `OrphanedFunctions`, `UtilitiesSprawl` | Fields: `issue_type` |
| `TestComplexity` | `TestComplexityHotspot` | Fields: `cyclomatic`, `cognitive` |
| `TestQuality` | `FlakyTestPattern`, `AssertionComplexity` | Fields: `issue_type` |
**Note**: Legacy types are categorized under `CodeQuality` by default (see `src/priority/mod.rs:251-252`).
## Debt Categories
The `DebtCategory` enum groups debt types into strategic categories:
**Source**: `src/priority/mod.rs:196-213`
```rust
pub enum DebtCategory {
Architecture, // Structure, design, complexity
Testing, // Coverage, test quality
Performance, // Speed, memory, efficiency
CodeQuality, // Maintainability, readability
}
```
### Category Mapping
**Source**: Category assignment logic in `src/priority/mod.rs:216-254`
| Architecture | GodObject, FeatureEnvy, PrimitiveObsession, ScatteredType, OrphanedFunctions, UtilitiesSprawl | Structural improvements, design patterns, type organization |
| Testing | TestingGap, TestComplexityHotspot, TestTodo, TestDuplication, AssertionComplexity, FlakyTestPattern | Test coverage, test quality |
| Performance | AsyncMisuse, CollectionInefficiency, NestedLoops, BlockingIO, AllocationInefficiency, StringConcatenation, SuboptimalDataStructure, ResourceLeak | Runtime efficiency, resource usage |
| CodeQuality | ComplexityHotspot, DeadCode, Duplication, Risk, ErrorSwallowing, MagicValues, *all legacy types* | Maintainability, reliability, code clarity |
**Note**: `GodObject` is a unified variant that handles all god object detection types (GodClass, GodFile, GodModule). There is no separate `GodModule` debt type in the enum.
**Language-Specific Debt Patterns:**
Some debt patterns only apply to languages with specific features:
- **BlockingIO, AsyncMisuse**: Async-capable languages (Rust)
- **AllocationInefficiency, ResourceLeak**: Languages with manual memory management (Rust)
- **Error handling patterns**: Vary by language error model (Result in Rust)
Debtmap automatically applies only the relevant debt patterns during analysis.
### Examples by Category
#### Architecture Debt
**GodObject** (unified variant for GodClass/GodFile/GodModule): Too many responsibilities
**Source**: `src/priority/debt_types.rs:143-154`
```rust
// God module: handles parsing, validation, storage, notifications
mod user_service {
fn parse_user() { /* ... */ }
fn validate_user() { /* ... */ }
fn save_user() { /* ... */ }
fn send_email() { /* ... */ }
fn log_activity() { /* ... */ }
// ... 20+ more functions
}
```
The `GodObject` debt type captures all god object patterns through a unified variant. The specific detection type (GodClass, GodFile, or GodModule) is stored in the parent `UnifiedDebtItem.god_object_indicators.detection_type` field:
| GodClass | Class with too many methods/fields | `methods`, `fields` (Some), `responsibilities` |
| GodFile | File with too many functions | `methods`, `fields` (None), `lines` |
| GodModule | Module with too many responsibilities | `methods`, `fields` (None), `responsibilities` |
**When detected**: Complexity-weighted scoring system (see detailed explanation below)
**Action**: Split into focused modules (parser, validator, repository, notifier)
#### Complexity-Weighted God Object Detection
Debtmap uses **complexity-weighted scoring** for god object detection to reduce false positives on well-refactored code. This ensures that a file with 100 simple helper functions doesn't rank higher than a file with 10 complex functions.
**The Problem:**
Traditional god object detection counts methods:
- File A: 100 methods (average complexity: 1.5) → Flagged as god object
- File B: 10 methods (average complexity: 17.0) → Not flagged
But File A might be a well-organized utility module with many small helpers, while File B is truly problematic with highly complex functions that need refactoring.
**The Solution:**
Debtmap weights each function by its cyclomatic complexity using this formula:
```
weight = (max(1, complexity) / 3)^1.5
```
**Weight Examples:**
- Simple helper (complexity 1): weight ≈ 0.19
- Baseline function (complexity 3): weight = 1.0
- Moderate function (complexity 9): weight ≈ 5.2
- Complex function (complexity 17): weight ≈ 13.5
- Critical function (complexity 33): weight ≈ 36.5
**God Object Score Calculation:**
```
weighted_method_count = sum(weight for each function)
complexity_penalty = 0.7 if avg_complexity < 3, 1.0 if 3-10, 1.5 if > 10
god_object_score = (
(weighted_method_count / threshold) * 40% +
(field_count / threshold) * 20% +
(responsibility_count / threshold) * 15% +
(lines_of_code / 500) * 25%
) * complexity_penalty
```
**Threshold**: God object detected if `score >= 70.0`
**Real-World Example:**
```
shared_cache.rs:
- 100 functions, average complexity: 1.5
- Weighted score: ~19.0 (100 * 0.19)
- God object score: 45.2
- Result: Not a god object ✓
legacy_parser.rs:
- 10 functions, average complexity: 17.0
- Weighted score: ~135.0 (10 * 13.5)
- God object score: 87.3
- Result: God object detected ✓
```
**Benefits:**
- **Reduces false positives** on utility modules with many simple functions
- **Focuses attention** on truly problematic complex modules
- **Rewards good refactoring** - breaking large functions into small helpers improves score
- **Aligns with reality** - complexity matters more than count for maintainability
**How to View:**
When Debtmap detects a god object, the output includes:
- Raw method count
- Weighted method count
- Average complexity
- God object score
- Recommended module splits based on responsibility clustering
#### Type Organization Debt
**Source**: Type organization patterns defined in `src/priority/debt_types.rs:189-205` (Spec 187), detection logic in `src/organization/codebase_type_analyzer.rs`
These patterns detect issues with how types and their associated functions are organized across the codebase.
**ScatteredType**: Type with methods scattered across multiple files
```rust
// Type definition in types/user.rs
pub struct User {
id: UserId,
name: String,
}
// Methods scattered across files:
// In modules/auth.rs:
impl User {
fn authenticate(&self) -> Result<Session> { /* ... */ }
}
// In modules/validation.rs:
impl User {
fn validate_email(&self) -> Result<()> { /* ... */ }
}
// In modules/persistence.rs:
impl User {
fn save(&self) -> Result<()> { /* ... */ }
}
// Problem: User methods spread across 4+ files!
```
**When detected**: Type has methods in 2+ files
**Severity levels**: Stored as String field derived from `file_count`:
- Low = 2 files
- Medium = 3-5 files
- High = 6+ files
**Action**: Consolidate methods into primary file or create focused trait implementations
**Source**: Detection criteria from `src/organization/codebase_type_analyzer.rs:46-47`, type definition in `src/priority/debt_types.rs:190-195`
**OrphanedFunctions**: Functions that should be methods on a type
```rust
// Bad: Orphaned functions operating on User
fn validate_user_email(user: &User) -> Result<()> {
// Email validation logic
}
fn calculate_user_age(user: &User) -> u32 {
// Age calculation from birthdate
}
fn format_user_display(user: &User) -> String {
// Display formatting
}
// Good: Functions converted to methods
impl User {
fn validate_email(&self) -> Result<()> { /* ... */ }
fn age(&self) -> u32 { /* ... */ }
fn display(&self) -> String { /* ... */ }
}
```
**When detected**: Multiple functions with shared type parameter pattern (e.g., all take `&User`)
**Action**: Convert functions to methods on the target type
**Source**: Detection in `src/organization/codebase_type_analyzer.rs:58-71`, type definition in `src/priority/debt_types.rs:196-200`
**UtilitiesSprawl**: Utility module with poor cohesion
```rust
// Bad: utils.rs with mixed responsibilities
mod utils {
fn parse_date(s: &str) -> Date { /* ... */ }
fn validate_email(s: &str) -> bool { /* ... */ }
fn calculate_hash(data: &[u8]) -> Hash { /* ... */ }
fn format_currency(amount: f64) -> String { /* ... */ }
fn send_notification(msg: &str) { /* ... */ }
// ... 20+ more unrelated functions
}
// Good: Focused modules
mod date_utils { fn parse(s: &str) -> Date { /* ... */ } }
mod validators { fn email(s: &str) -> bool { /* ... */ } }
mod crypto { fn hash(data: &[u8]) -> Hash { /* ... */ } }
mod formatters { fn currency(amount: f64) -> String { /* ... */ } }
mod notifications { fn send(msg: &str) { /* ... */ } }
```
**When detected**: Utility module has many functions operating on diverse types with low cohesion
**Action**: Split into focused modules based on domain or responsibility
**Source**: Detection in `src/organization/codebase_type_analyzer.rs:74-80`, type definition in `src/priority/debt_types.rs:201-204`
#### Testing Debt
**TestingGap**: Functions with insufficient test coverage
```rust
// 0% coverage - critical business logic untested
fn calculate_tax(amount: f64, region: &str) -> f64 {
// Complex tax calculation logic
// No tests exist for this function!
}
```
**When detected**: Coverage data shows function has < 80% line coverage
**Action**: Add unit tests to cover all branches and edge cases
**TestComplexity**: Test functions too complex
```rust
#[test]
fn complex_test() {
// Cyclomatic: 12 (too complex for a test)
for input in test_cases {
if input.is_special() {
match input.type {
/* complex test logic */
}
}
}
}
```
**When detected**: Test functions with cyclomatic > 10 or cognitive > 15
**Action**: Split into multiple focused tests, use test fixtures
**FlakyTestPattern**: Non-deterministic tests
```rust
#[test]
fn flaky_test() {
let result = async_operation().await; // Timing-dependent
thread::sleep(Duration::from_millis(100)); // Race condition!
assert_eq!(result.status, "complete");
}
```
**When detected**: Pattern analysis for timing dependencies, random values
**Action**: Use mocks, deterministic test data, proper async test utilities
#### Performance Debt
**AllocationInefficiency**: Excessive allocations
```rust
// Bad: Allocates on every iteration
fn process_items(items: &[Item]) -> Vec<String> {
let mut results = Vec::new();
for item in items {
results.push(item.name.clone()); // Unnecessary clone
}
results
}
// Good: Pre-allocate, avoid clones
fn process_items(items: &[Item]) -> Vec<&str> {
items.iter().map(|item| item.name.as_str()).collect()
}
```
**BlockingIO**: Blocking operations in async contexts
```rust
// Bad: Blocks async runtime
async fn load_data() -> Result<Data> {
let file = std::fs::read_to_string("data.json")?; // Blocking!
parse_json(&file)
}
// Good: Async I/O
async fn load_data() -> Result<Data> {
let file = tokio::fs::read_to_string("data.json").await?;
parse_json(&file)
}
```
**NestedLoops**: O(n²) or worse complexity
```rust
// Bad: O(n²) nested loops
fn find_duplicates(items: &[Item]) -> Vec<(Item, Item)> {
let mut dupes = vec![];
for i in 0..items.len() {
for j in i+1..items.len() {
if items[i] == items[j] {
dupes.push((items[i].clone(), items[j].clone()));
}
}
}
dupes
}
// Good: O(n) with HashSet
fn find_duplicates(items: &[Item]) -> Vec<Item> {
let mut seen = HashSet::new();
items.iter().filter(|item| !seen.insert(item)).cloned().collect()
}
```
#### Code Quality Debt
**ComplexityHotspot**: Functions exceeding complexity thresholds
**Source**: Type definition in `src/priority/debt_types.rs:84-87`, categorized as CodeQuality in `src/priority/mod.rs:245`
```rust
// Cyclomatic: 22, Cognitive: 35
fn process_transaction(tx: Transaction, account: &mut Account) -> Result<Receipt> {
if tx.amount <= 0 {
return Err(Error::InvalidAmount);
}
if account.balance < tx.amount {
if account.overdraft_enabled {
if account.overdraft_limit >= tx.amount {
// More nested branches...
}
} else {
return Err(Error::InsufficientFunds);
}
}
// ... deeply nested logic with many branches
Ok(receipt)
}
```
**Fields captured**: `cyclomatic: u32`, `cognitive: u32`
**When detected**: Cyclomatic > 10 OR Cognitive > 15 (configurable)
**Action**: Break into smaller functions, extract validation, simplify control flow
**DeadCode**: Unreachable or unused code
**Source**: Type definition in `src/priority/debt_types.rs:88-93`, categorized as CodeQuality in `src/priority/mod.rs:246`
```rust
// Private function never called within module
fn obsolete_calculation(x: i32) -> i32 {
x * 2 + 5 // Dead code - no callers
}
// Public function but no external usage
pub fn deprecated_api(data: &str) -> Result<()> {
// Unreachable in practice
Ok(())
}
```
**When detected**: Function visibility analysis + call graph shows no callers
**Action**: Remove dead code or document if intentionally kept for future use
**MagicValues**: Unexplained literal values
**Source**: Type definition in `src/priority/debt_types.rs:163-166`, categorized as CodeQuality in `src/priority/mod.rs:250`
```rust
// Bad: Magic numbers
fn calculate_price(quantity: u32) -> f64 {
quantity as f64 * 19.99 + 5.0 // What are these numbers?
}
// Good: Named constants
const UNIT_PRICE: f64 = 19.99;
const SHIPPING_COST: f64 = 5.0;
fn calculate_price(quantity: u32) -> f64 {
quantity as f64 * UNIT_PRICE + SHIPPING_COST
}
```
**When detected**: Numeric or string literals without clear context (excludes 0, 1, common patterns)
**Action**: Extract to named constants or configuration
**Duplication**: Duplicated code blocks
```rust
// File A:
fn process_user(user: User) -> Result<()> {
validate_email(&user.email)?;
validate_age(user.age)?;
save_to_database(&user)?;
send_welcome_email(&user.email)?;
Ok(())
}
// File B: Duplicated validation
fn process_admin(admin: Admin) -> Result<()> {
validate_email(&admin.email)?; // Duplicated
validate_age(admin.age)?; // Duplicated
save_to_database(&admin)?;
grant_admin_privileges(&admin)?;
Ok(())
}
```
**When detected**: Similar code blocks > 50 lines (configurable)
**Action**: Extract shared code into reusable functions
**ErrorSwallowing**: Errors caught but ignored
```rust
// Bad: Error swallowed, no context
match risky_operation() {
Ok(result) => process(result),
Err(_) => {}, // Silent failure!
}
// Good: Error handled with context
match risky_operation() {
Ok(result) => process(result),
Err(e) => {
log::error!("Risky operation failed: {}", e);
return Err(e.into());
}
}
```
**When detected**: Empty catch blocks, ignored Results
**Action**: Add proper error logging and propagation
**Risk**: High-risk code (complex + poorly tested)
```rust
// Cyclomatic: 18, Coverage: 20%, Risk Score: 47.6 (HIGH)
fn process_payment(tx: Transaction) -> Result<Receipt> {
// Complex payment logic with minimal testing
// High risk of bugs in production
}
```
**When detected**: Combines complexity metrics with coverage data
**Action**: Either add comprehensive tests OR refactor to reduce complexity
### Debt Scoring Formula
Each debt item gets a score based on priority and type:
```
debt_score = priority_weight × type_weight
```
**Priority weights:**
- Low = 1
- Medium = 3
- High = 5
- Critical = 10
**Combined examples:**
- Low Todo = 1 × 1 = 1
- Medium Fixme = 3 × 2 = 6
- High Complexity = 5 × 5 = 25
- Critical Complexity = 10 × 5 = 50
**Total debt score** = Sum of all debt item scores
Lower is better. Track over time to measure improvement.