Expand description
§Debtmap
A code complexity and technical debt analyzer that identifies which code to refactor for maximum cognitive debt reduction and which code to test for maximum risk reduction.
§Why Debtmap?
Unlike traditional static analysis tools that simply flag complex code, debtmap answers two critical questions:
- “What should I refactor to reduce cognitive burden?” - Identifies overly complex code that slows down development
- “What should I test first to reduce the most risk?” - Pinpoints untested complex code that threatens stability
Unique Capabilities:
- Coverage-Risk Correlation - Combines complexity metrics with test coverage to identify genuinely risky code (high complexity + low coverage = critical risk)
- Reduced False Positives - Uses entropy analysis and pattern detection to distinguish genuinely complex code from repetitive patterns, reducing false positives by up to 70%
- Actionable Recommendations - Provides specific guidance with quantified impact metrics instead of generic warnings
- Multi-Factor Analysis - Analyzes complexity, coverage, dependencies, and call graphs for comprehensive prioritization
- Fast & Open Source - Written in Rust for 10-100x faster analysis, MIT licensed
§Quick Start
§Basic File Analysis
use debtmap::{analyzers::get_analyzer, Language};
// Get language-specific analyzer
let analyzer = get_analyzer(Language::Rust);
// Parse source code
let content = r#"
fn example() {
if true {
println!("hello");
}
}
"#;
let ast = analyzer.parse(&content, "example.rs".into()).unwrap();
// Analyze complexity metrics
let metrics = analyzer.analyze(&ast);
println!("Functions analyzed: {}", metrics.complexity.functions.len());
if !metrics.complexity.functions.is_empty() {
let avg = metrics.complexity.functions.iter()
.map(|f| f.cyclomatic as f64).sum::<f64>()
/ metrics.complexity.functions.len() as f64;
println!("Average complexity: {:.2}", avg);
}§Code Smell Detection
use debtmap::debt::patterns::find_code_smells;
use std::path::Path;
let content = r#"
fn example() {
// TODO: Fix this later
let x = 1;
}
"#;
// Find TODOs, FIXMEs, and other code smells
let smells = find_code_smells(&content, Path::new("example.rs"));
for smell in smells {
println!("{:?} at line {}", smell.debt_type, smell.line);
}§Coverage-Based Risk Analysis
use debtmap::{
analyzers::get_analyzer,
risk::{lcov::parse_lcov_file, RiskAnalyzer},
Language,
};
use std::path::PathBuf;
// Parse coverage data (skip if file doesn't exist)
let coverage_path = std::path::Path::new("target/coverage/lcov.info");
if !coverage_path.exists() {
println!("Generate coverage with: cargo llvm-cov --lcov --output-path target/coverage/lcov.info");
return;
}
let coverage_data = parse_lcov_file(coverage_path).unwrap();
// Analyze a file
let analyzer = get_analyzer(Language::Rust);
let content = std::fs::read_to_string("src/main.rs").unwrap();
let ast = analyzer.parse(&content, "src/main.rs".into()).unwrap();
let metrics = analyzer.analyze(&ast);
// Calculate risk scores for each function
let risk_analyzer = RiskAnalyzer::default();
let file_path = std::path::Path::new("src/main.rs");
for func in &metrics.complexity.functions {
let coverage = coverage_data.get_function_coverage(file_path, &func.name);
let risk = risk_analyzer.analyze_function(
PathBuf::from("src/main.rs"),
func.name.clone(),
(func.start_line, func.end_line),
&func.complexity,
coverage,
false,
);
if risk.risk_score > 5.0 {
println!("HIGH RISK: {} (score: {:.1})", risk.function_name, risk.risk_score);
}
}§Features
§Multi-Language Support
Debtmap analyzes code across multiple programming languages:
- Rust - Full support with comprehensive AST analysis using
syn - Python - Partial support via
rustpython-parser
§Performance Characteristics
- Parallel Processing - Uses
rayonfor CPU-intensive analysis across multiple files - Concurrent Data Structures - Leverages
dashmapfor lock-free concurrent access - Immutable Collections - Uses
imcrate for persistent data structures - Performance - 10-100x faster than Java/Python-based competitors
§Coverage Integration
Debtmap works with any tool generating LCOV format:
- Rust:
cargo-llvm-cov(recommended),cargo-tarpaulin - Python:
pytest-cov,coverage.py - JavaScript:
jest --coverage,nyc
§Architecture
Debtmap follows a functional architecture with clear separation of concerns:
┌─────────────────────────────────────────────────────────────┐
│ Input Layer (I/O) │
│ File Discovery → Content Reading → Coverage Parsing │
└────────────────────────┬────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Parser Layer (Pure) │
│ Language Detection → AST Generation → Symbol Extraction │
└────────────────────────┬────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Analysis Layer (Pure) │
│ Complexity → Debt Detection → Risk Assessment → Dependency │
└────────────────────────┬────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Aggregation Layer (Functional) │
│ Combine Results → Priority Scoring → Recommendation Gen │
└────────────────────────┬────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Output Layer (I/O) │
│ Format Selection → Report Generation → File Writing │
└─────────────────────────────────────────────────────────────┘§Core Modules
-
analyzers- Language-specific parsers and AST analysisanalyzers::get_analyzer- Factory for language-specific analyzersanalyzers::analyze_file- High-level file analysis API
-
debt- Technical debt pattern detectiondebt::patterns- Code smell detection (TODOs, magic numbers, etc.)debt::smells- Function-level smell analysis (long methods, deep nesting)debt::duplication- Duplicate code detectiondebt::coupling- Module coupling analysis
-
risk- Risk assessment and prioritizationrisk::RiskAnalyzer- Coverage-based risk scoringrisk::lcov::parse_lcov_file- LCOV format parserrisk::insights::generate_risk_insights- Actionable recommendation generation
-
complexity- Complexity metric calculations- Cyclomatic complexity (control flow branching)
- Cognitive complexity (human comprehension difficulty)
- Halstead metrics (vocabulary and volume)
-
io- Input/output formatting and handlingio::output- Multiple output formats (JSON, YAML, table)io::output::OutputWriter- Trait for custom output formats
-
analysis- Advanced analysis algorithmsanalysis::RustCallGraph- Function call graph constructionanalysis::DeadCodeAnalysis- Unused code detectionanalysis::FrameworkPatternDetector- Framework-specific patterns
-
testing- Test quality analysis- Test coverage correlation
- Test effectiveness scoring
§Data Flow Principles
Debtmap is built on functional programming principles:
- Pure Core - All analysis logic is pure functions with no side effects
- I/O at Boundaries - File operations and network calls isolated to edges
- Immutable Data - Uses persistent data structures for safe concurrent access
- Function Composition - Complex behavior built from simple, testable units
- Parallel Processing - Embarrassingly parallel analysis across files
§CLI Usage
For command-line usage, see the CLI Reference.
# Basic analysis
debtmap analyze .
# With coverage integration
cargo llvm-cov --lcov --output-path target/coverage/lcov.info
debtmap analyze . --lcov target/coverage/lcov.info
# Generate JSON report
debtmap analyze . --format json --output report.json§Examples
§Custom Complexity Thresholds
use debtmap::{analyzers::get_analyzer, Language};
let analyzer = get_analyzer(Language::Rust);
let content = std::fs::read_to_string("src/main.rs").unwrap();
let ast = analyzer.parse(&content, "src/main.rs".into()).unwrap();
let metrics = analyzer.analyze(&ast);
// Filter functions by custom complexity threshold
let high_complexity_threshold = 10;
let complex_functions: Vec<_> = metrics.complexity.functions.iter()
.filter(|f| f.cyclomatic > high_complexity_threshold)
.collect();
println!("Found {} highly complex functions", complex_functions.len());
for func in complex_functions {
println!(" {} (complexity: {})", func.name, func.cyclomatic);
}§Detecting Circular Dependencies
use debtmap::debt::circular::analyze_module_dependencies;
use debtmap::core::Dependency;
use std::path::PathBuf;
// Example: Analyze module dependencies from parsed files
// In practice, you would gather dependencies during file parsing
let files: Vec<(PathBuf, Vec<Dependency>)> = vec![
(PathBuf::from("src/main.rs"), vec![]),
(PathBuf::from("src/lib.rs"), vec![]),
];
let _dependency_graph = analyze_module_dependencies(&files);
// The dependency graph can be used to detect circular dependencies
// and analyze module coupling§Generating Risk Insights
use debtmap::{
analyzers::get_analyzer,
risk::{lcov::parse_lcov_file, RiskAnalyzer, insights::generate_risk_insights},
Language,
};
use std::path::PathBuf;
use im::Vector;
// Parse coverage and analyze file
let coverage = parse_lcov_file(std::path::Path::new("target/coverage/lcov.info")).unwrap();
let analyzer = get_analyzer(Language::Rust);
let content = std::fs::read_to_string("src/main.rs").unwrap();
let ast = analyzer.parse(&content, "src/main.rs".into()).unwrap();
let metrics = analyzer.analyze(&ast);
// Calculate risks for all functions
let risk_analyzer = RiskAnalyzer::default();
let mut risks = Vector::new();
let file_path = std::path::Path::new("src/main.rs");
for func in &metrics.complexity.functions {
let coverage_pct = coverage.get_function_coverage(file_path, &func.name);
let risk = risk_analyzer.analyze_function(
PathBuf::from("src/main.rs"),
func.name.clone(),
(func.start_line, func.end_line),
&func.complexity,
coverage_pct,
false,
);
risks.push_back(risk);
}
// Generate actionable insights
let insights = generate_risk_insights(risks, &risk_analyzer);
// Display top recommendations
for rec in insights.risk_reduction_opportunities.iter().take(5) {
println!("{}", rec.recommendation);
}§Resources
- Documentation: iepathos.github.io/debtmap
- Repository: github.com/iepathos/debtmap
- Crate: crates.io/crates/debtmap
- Issues: github.com/iepathos/debtmap/issues
§License
Debtmap is licensed under the MIT License.
Re-exports§
pub use crate::core::AnalysisResults;pub use crate::core::CircularDependency;pub use crate::core::ComplexityMetrics;pub use crate::core::ComplexityReport;pub use crate::core::ComplexitySummary;pub use crate::core::DebtItem;pub use crate::core::DebtType;pub use crate::core::Dependency;pub use crate::core::DependencyKind;pub use crate::core::DependencyReport;pub use crate::core::DuplicationBlock;pub use crate::core::DuplicationLocation;pub use crate::core::FileMetrics;pub use crate::core::FunctionMetrics;pub use crate::core::Language;pub use crate::core::ModuleDependency;pub use crate::core::Priority;pub use crate::core::TechnicalDebtReport;pub use crate::debt::circular::analyze_module_dependencies;pub use crate::debt::circular::DependencyGraph;pub use crate::debt::coupling::calculate_coupling_metrics;pub use crate::debt::coupling::identify_coupling_issues;pub use crate::debt::coupling::CouplingMetrics;pub use crate::debt::duplication::detect_duplication;pub use crate::debt::patterns::detect_duplicate_strings;pub use crate::debt::patterns::find_code_smells;pub use crate::debt::patterns::find_code_smells_with_suppression;pub use crate::debt::patterns::find_todos_and_fixmes;pub use crate::debt::patterns::find_todos_and_fixmes_with_suppression;pub use crate::debt::smells::analyze_function_smells;pub use crate::debt::smells::analyze_module_smells;pub use crate::debt::smells::detect_deep_nesting;pub use crate::debt::smells::detect_long_method;pub use crate::debt::smells::detect_long_parameter_list;pub use crate::debt::smells::CodeSmell;pub use crate::debt::smells::SmellType;pub use crate::debt::suppression::parse_suppression_comments;pub use crate::debt::suppression::SuppressionContext;pub use crate::debt::suppression::SuppressionStats;pub use crate::core::metrics::calculate_average_complexity;pub use crate::core::metrics::count_high_complexity;pub use crate::core::metrics::find_max_complexity;pub use crate::io::output::create_writer;pub use crate::io::output::OutputFormat;pub use crate::io::output::OutputWriter;pub use crate::analyzers::analyze_file;pub use crate::analyzers::get_analyzer;pub use crate::analyzers::Analyzer;pub use crate::risk::insights::generate_risk_insights;pub use crate::risk::lcov::parse_lcov_file;pub use crate::risk::FunctionRisk;pub use crate::risk::RiskAnalyzer;pub use crate::risk::RiskCategory;pub use crate::risk::RiskInsight;pub use crate::risk::TestingRecommendation;pub use crate::analysis::AnalysisConfig;pub use crate::analysis::CrossModuleTracker;pub use crate::analysis::DeadCodeAnalysis;pub use crate::analysis::FrameworkPatternDetector;pub use crate::analysis::FunctionPointerTracker;pub use crate::analysis::RustCallGraph;pub use crate::analysis::RustCallGraphBuilder;pub use crate::analysis::TraitRegistry;pub use crate::effects::combine_validations;pub use crate::effects::effect_fail;pub use crate::effects::effect_from_fn;pub use crate::effects::effect_pure;pub use crate::effects::run_effect;pub use crate::effects::run_effect_async;pub use crate::effects::run_validation;pub use crate::effects::validation_failure;pub use crate::effects::validation_failures;pub use crate::effects::validation_map;pub use crate::effects::validation_success;pub use crate::effects::AnalysisEffect;pub use crate::effects::AnalysisErrors;pub use crate::effects::AnalysisValidation;pub use crate::env::AnalysisEnv;pub use crate::env::RealEnv;pub use crate::errors::errors_to_anyhow;pub use crate::errors::format_error_list;pub use crate::errors::AnalysisError;pub use crate::effects::ask_env;pub use crate::effects::asks_config;pub use crate::effects::asks_entropy;pub use crate::effects::asks_scoring;pub use crate::effects::asks_thresholds;pub use crate::effects::local_with_config;pub use crate::resources::bracket_io;pub use crate::resources::with_file_read;pub use crate::resources::with_lock_file;pub use crate::resources::with_progress;pub use crate::resources::with_spinner;pub use crate::resources::with_temp_dir;pub use crate::resources::FileHandle;pub use crate::resources::LockFile;pub use crate::resources::ProgressHandle;pub use crate::resources::TempDir;pub use crate::testkit::ConfigBuilder;pub use crate::testkit::DebtmapTestEnv;
Modules§
- analysis
- Advanced Analysis Module
- analysis_
utils - analyzers
- builders
- cli
- commands
- common
- comparison
- complexity
- config
- context
- Context-aware detection system for reducing false positives
- core
- data_
flow - database
- debt
- effects
- Effect type aliases and helpers for debtmap analysis.
- env
- Environment trait and implementations for debtmap analysis.
- errors
- Unified error types for debtmap analysis operations.
- extraction_
patterns - formatting
- io
- metrics
- Metrics calculation module
- organization
- output
- patterns
- pipeline
- Pure functional pipeline for technical debt analysis.
- priority
- progress
- Progress feedback infrastructure for debtmap analysis.
- refactoring
- resource
- resources
- Bracket pattern for resource management (Spec 206).
- risk
- testing
- testkit
- Testing infrastructure for debtmap using stillwater’s MockEnv.
- transformers
- tui
- Terminal User Interface (TUI) for debtmap analysis progress.
- utils
Macros§
- assert_
contains_ error - Assert that an error message contains a specific pattern.
- assert_
result_ err - Assert that a Result is Err and extract the error.
- assert_
result_ ok - Assert that a Result is Ok and extract the value.
- assert_
validation_ err - Assert that a Validation has failures and extract the errors.
- assert_
validation_ error_ count - Assert that a Validation has a specific number of errors.
- assert_
validation_ ok - Assert that a Validation is successful and extract the value.