Skip to content

Latest commit

 

History

History
440 lines (323 loc) · 12.2 KB

File metadata and controls

440 lines (323 loc) · 12.2 KB

Contributing to Debtmap

Thank you for your interest in contributing to Debtmap! This guide will help you get started with development, testing, and submitting contributions.

Getting Started

Prerequisites

Before you begin, ensure you have the following installed:

  • Rust (latest stable version): Install via rustup
  • Just (command runner): Install with cargo install just
  • Git: For version control

Optional but recommended:

  • cargo-nextest: Faster test runner - cargo install cargo-nextest
  • cargo-llvm-cov: Code coverage - cargo install cargo-llvm-cov
  • cargo-watch: Auto-rebuild on file changes - cargo install cargo-watch

Clone and Build

# Clone the repository
git clone https://github.com/iepathos/debtmap.git
cd debtmap

# Build the project
cargo build

# Run tests to verify everything works
just test

# Try analyzing debtmap itself
cargo build --bin debtmap
./target/debug/debtmap analyze . --top 10

Development Workflow

Using Just Commands

Debtmap uses Just for common development tasks. Run just or just --list to see all available commands:

# Development
just dev            # Run in development mode
just watch          # Run with hot reloading
just build          # Build the project

# Testing
just test           # Run all tests with nextest
just test-verbose   # Run tests with output
just coverage       # Generate coverage report
just analyze-self   # Analyze debtmap with coverage

# Code Quality
just fmt            # Format code with rustfmt
just lint           # Run clippy linter
just check          # Quick syntax check

# CI Simulation
just ci             # Run all CI checks locally
just pre-commit     # Run pre-commit checks

Feature Branch Workflow

  1. Create a feature branch from master:

    git checkout -b feature/your-feature-name
  2. Make your changes following the code style guidelines below

  3. Run tests and linters:

    just fmt
    just lint
    just test
  4. Commit your changes with clear, descriptive messages:

    git add .
    git commit -m "Add feature: description of what changed"
  5. Push to your fork and create a pull request

Code Style

Debtmap follows functional programming principles and Rust best practices. Please review CLAUDE.md for detailed coding guidelines.

Key Principles

  • Functional-first design: Prefer pure functions over stateful methods
  • Immutable data structures: Use the im crate for persistent collections
  • Composable pipelines: Chain transformations using iterators
  • Error handling: Use Result<T> with the ? operator and anyhow::Context
  • Maximum function length: 20 lines (prefer 5-10)
  • Maximum cyclomatic complexity: 5

Formatting and Linting

Before committing, always run:

just fmt    # Auto-format code with rustfmt
just lint   # Check for clippy warnings (must pass with -D warnings)

Required standards:

  • All code must be formatted with cargo fmt
  • All clippy warnings must be addressed (no warnings allowed)
  • Public APIs must have documentation comments
  • Complex logic should have inline comments explaining "why"

Example: Good Function Design

// Good: Pure, composable, single responsibility
fn calculate_complexity_score(metrics: &FunctionMetrics) -> f64 {
    let cyclomatic_weight = metrics.cyclomatic as f64 / 10.0;
    let cognitive_weight = metrics.cognitive as f64 / 20.0;
    (cyclomatic_weight + cognitive_weight) * 5.0
}

// Good: Clear error handling
fn parse_lcov_file(path: &Path) -> Result<CoverageData> {
    let content = fs::read_to_string(path)
        .context("Failed to read LCOV file")?;

    parse_lcov_content(&content)
        .context("Failed to parse LCOV content")
}

// Avoid: Long, complex functions with mixed concerns
fn bad_example(data: Vec<u8>) -> Option<String> {
    // Don't do this - mixes parsing, validation, I/O, and error handling
    // in one large function with unclear error handling
    ...
}

Testing Requirements

All new features and bug fixes must include tests.

Writing Tests

  • Unit tests: Place in #[cfg(test)] modules in the same file as the code
  • Integration tests: Add to tests/ directory
  • Test naming: Use descriptive names that explain what's being tested
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn complexity_score_increases_with_cyclomatic() {
        let metrics = FunctionMetrics {
            cyclomatic: 10,
            cognitive: 5,
            ..Default::default()
        };
        let score = calculate_complexity_score(&metrics);
        assert!(score > 0.0);
    }

    #[test]
    fn lcov_parser_handles_empty_file() {
        let result = parse_lcov_content("");
        assert!(result.is_ok());
        assert_eq!(result.unwrap().functions.len(), 0);
    }
}

Running Tests

# Run all tests
just test

# Run with verbose output
just test-verbose

# Run specific test pattern
just test-pattern "complexity"

# Run with coverage
just coverage
just coverage-open  # Opens HTML report

Test Coverage Expectations

  • Aim for 85%+ code coverage for new features
  • Critical paths (scoring, analysis) should have 95%+ coverage
  • Pure functions are easy to test - no excuses for low coverage
  • Integration tests should cover end-to-end workflows

Pull Request Process

Before Submitting

  1. Ensure all tests pass:

    just ci  # Runs full CI suite locally
  2. Update documentation if you've changed APIs or added features

  3. Add yourself to contributors (if this is your first PR)

PR Guidelines

  • Title: Clear, descriptive summary of changes

  • Description: Explain what changed and why

    • Link to related issues: Fixes #123 or Relates to #456
    • Describe the problem being solved
    • Explain your approach
    • Note any breaking changes
  • Commits:

    • Use clear, descriptive commit messages
    • Follow conventional commits if possible: feat:, fix:, docs:, test:
    • Each commit should be a logical, atomic change

Review Process

  • Maintainers will review your PR and may request changes
  • Address feedback by pushing new commits (don't force-push)
  • Once approved, a maintainer will merge your PR
  • PRs require:
    • All CI checks passing (tests, linting, formatting)
    • At least one maintainer approval
    • No merge conflicts with master

Architecture Overview

Debtmap is organized into focused modules with clear responsibilities:

src/
├── analyzers/      # Language-specific AST parsers
├── core/           # Core data types and utilities
├── debt/           # Technical debt pattern detection
├── complexity/     # Complexity metrics calculation
├── risk/           # Risk analysis and coverage integration
├── io/             # File I/O and output formatting
└── testing/        # Test analysis and quality metrics

For detailed architecture documentation, see the Architecture Guide.

Improving Rust Analysis

To contribute to Rust analysis depth:

  1. Add new analysis in src/analyzers/rust.rs or related modules
  2. Implement trait methods or extend existing analyzers
  3. Add comprehensive tests in tests/rust_tests.rs or module-specific tests
  4. Update documentation with examples

Example areas for improvement:

// Improving macro expansion analysis
pub fn analyze_macro_expansion(item: &syn::Item) -> Result<MacroMetrics> {
    // Detect macro invocations and their complexity impact
}

// Better trait resolution
pub fn resolve_trait_impl(impl_item: &syn::ItemImpl) -> Result<TraitContext> {
    // Track trait implementations and their relationships
}

// Lifetime analysis
pub fn analyze_lifetimes(sig: &syn::Signature) -> LifetimeComplexity {
    // Measure complexity introduced by lifetime annotations
}

Finding Good First Issues

New to Debtmap? Look for issues labeled:

  • good-first-issue - Beginner-friendly issues
  • help-wanted - Issues where contributions are especially welcome
  • documentation - Improve docs (great for first-time contributors)

Recommended First Contributions

  • Documentation improvements: Fix typos, clarify confusing sections, add Rust examples
  • Test coverage: Add tests for untested Rust analysis code paths
  • Bug fixes: Start with issues tagged bug and good-first-issue
  • Rust analysis depth: Improve macro expansion, trait resolution, lifetime analysis
  • New Rust metrics: Implement additional Rust-specific complexity or quality metrics
  • Rust patterns: Detect more Rust idioms and anti-patterns
  • Performance: Optimize Rust analysis algorithms

Note on multi-language support: Debtmap is currently focusing exclusively on Rust analysis. Multi-language support (Python, JavaScript/TypeScript, Go, etc.) will be considered once Rust analysis reaches maturity. If you're interested in contributing to multi-language support in the future, please open an issue to discuss the roadmap and timeline.

Communication

Where to Ask Questions

  • GitHub Discussions: For general questions, ideas, and discussions
  • GitHub Issues: For bug reports and feature requests
  • Pull Requests: For code review and implementation discussions

Getting Help

Stuck? Don't hesitate to ask:

  1. Check the documentation
  2. Search existing issues and discussions
  3. Open a new discussion or issue with:
    • What you're trying to do
    • What you've tried so far
    • Any error messages or unexpected behavior

Development Tips

Performance Profiling

# Build with debug symbols
cargo build --release --profile release-with-debug

# Profile with perf (Linux)
perf record --call-graph=dwarf ./target/release/debtmap analyze .
perf report

# Use cargo-flamegraph
cargo install flamegraph
cargo flamegraph -- analyze .

Debugging

# Run with debug logging
RUST_LOG=debug cargo run -- analyze .

# Run with backtrace
RUST_BACKTRACE=1 cargo run -- analyze .

# Use cargo-expand to debug macros
cargo expand

Useful Development Commands

# Check for unused dependencies
just unused-deps

# Find security vulnerabilities
just audit

# Check for duplicate dependencies
just duplicate-deps

# Generate and open documentation
just doc

Functional Programming in Rust

Debtmap emphasizes functional programming patterns. When contributing, prefer:

Iterators over loops

// Good
let scores: Vec<f64> = functions
    .iter()
    .map(|f| calculate_score(f))
    .collect();

// Avoid
let mut scores = Vec::new();
for func in &functions {
    scores.push(calculate_score(func));
}

Immutable transformations

// Good - returns new data
fn add_coverage(mut metrics: FileMetrics, coverage: f64) -> FileMetrics {
    metrics.coverage = coverage;
    metrics
}

// Avoid - mutates in place
fn add_coverage_mut(metrics: &mut FileMetrics, coverage: f64) {
    metrics.coverage = coverage;
}

Pure functions

// Good - pure function, easy to test
fn calculate_risk_score(complexity: u32, coverage: f64) -> f64 {
    let complexity_factor = complexity as f64 / 10.0;
    let coverage_factor = 1.0 - coverage;
    complexity_factor * coverage_factor * 10.0
}

// Avoid - side effects, harder to test
fn calculate_risk_score_bad(func: &Function) -> f64 {
    println!("Calculating for {}", func.name); // Side effect!
    // ... calculation
}

For complete guidelines, see CLAUDE.md in the repository root.

License

By contributing to Debtmap, you agree that your contributions will be licensed under the MIT License.

Code of Conduct

Please note that this project is released with a Code of Conduct. By participating in this project you agree to abide by its terms.

Thank You!

Your contributions make Debtmap better for everyone. Whether you're fixing bugs, adding features, improving documentation, or helping others in discussions - thank you for being part of the community!

If you have questions or need help getting started, don't hesitate to reach out through GitHub Discussions or Issues. We're here to help!