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

Boilerplate vs Complexity

Overview

Debtmap distinguishes between boilerplate code (necessary but mechanical patterns) and true complexity (business logic requiring cognitive effort). This distinction is critical for:

  • Avoiding false positives in complexity analysis
  • Focusing refactoring efforts on actual problems
  • Understanding which high-complexity code is acceptable
  • Providing actionable recommendations

This chapter explains how Debtmap identifies boilerplate patterns, why they differ from complexity, and how to interpret the analysis results.

The Distinction

What is Boilerplate?

Boilerplate code consists of repetitive, mechanical patterns that are:

  1. Required by language/framework - Type conversions, trait implementations, builder patterns
  2. Structurally necessary - Match arms for enums, error propagation, validation chains
  3. Low cognitive load - Pattern-based code that developers scan rather than deeply analyze
  4. Not actual complexity - High cyclomatic complexity but mechanistic structure

Examples:

  • From trait implementations converting between types
  • Display formatting with exhaustive enum match arms
  • Builder pattern setters with validation
  • Error conversion implementations
  • Serialization/deserialization code

What is True Complexity?

True complexity consists of business logic that requires:

  1. Domain understanding - Knowledge of problem space and requirements
  2. Cognitive effort - Careful analysis to understand behavior
  3. Algorithmic decisions - Non-obvious control flow or data transformations
  4. Maintainability risk - Changes may introduce subtle bugs

Examples:

  • Graph traversal algorithms
  • Complex business rules with multiple conditions
  • State machine implementations with non-trivial transitions
  • Performance-critical optimizations
  • Error recovery with fallback strategies

Real Example: ripgrep’s defs.rs

The ripgrep codebase provides an excellent real-world example of boilerplate vs complexity.

File: crates/printer/src/defs.rs

This file contains type conversion implementations with high cyclomatic complexity scores but minimal actual complexity:

#![allow(unused)]
fn main() {
impl From<HyperlinkFormat> for ColorHyperlink {
    fn from(format: HyperlinkFormat) -> ColorHyperlink {
        match format {
            HyperlinkFormat::Default => ColorHyperlink::default(),
            HyperlinkFormat::Grep => ColorHyperlink::grep(),
            HyperlinkFormat::GrepPlus => ColorHyperlink::grep_plus(),
            HyperlinkFormat::Ripgrep => ColorHyperlink::ripgrep(),
            HyperlinkFormat::FileNone => ColorHyperlink::file_none(),
            // ... 10+ more variants
        }
    }
}
}

Analysis:

  • Cyclomatic Complexity: 15+ (one branch per enum variant)
  • Cognitive Complexity: Low (simple delegation pattern)
  • Boilerplate Confidence: 95% (trait implementation with mechanical structure)

Why This Matters

Without boilerplate detection, this file would be flagged as:

  • High complexity debt
  • Requiring refactoring
  • Priority for review

With boilerplate detection, it’s correctly classified as:

  • Necessary type conversion code
  • Low maintenance risk
  • Can be safely skipped in debt prioritization

Detection Methodology

Debtmap uses a multi-phase analysis pipeline to detect boilerplate:

Phase 1: Trait Analysis

Identifies trait implementations known to produce boilerplate:

High-confidence boilerplate traits:

  • From, Into - Type conversions
  • Display, Debug - Formatting
  • Default - Default value construction
  • Clone, Copy - Value semantics
  • Eq, PartialEq, Ord, PartialOrd - Comparisons
  • Hash - Hashing implementations

Medium-confidence boilerplate traits:

  • Serialize, Deserialize - Serialization
  • AsRef, AsMut, Deref, DerefMut - Reference conversions
  • Custom builder traits

See src/debt/boilerplate/boilerplate_traits.rs:10-58 for complete trait categorization.

Phase 2: Pattern Analysis

Analyzes code structure for boilerplate patterns:

Pattern 1: Simple Delegation

#![allow(unused)]
fn main() {
fn operation(&self) -> Result<T> {
    self.inner.operation()  // Single delegation call
}
}

Score: 90% confidence

Pattern 2: Trivial Match Arms

#![allow(unused)]
fn main() {
match variant {
    A => handler_a(),
    B => handler_b(),
    C => handler_c(),
}
}

Each arm calls a single function with no additional logic. Score: 85% confidence

Pattern 3: Validation Chains

#![allow(unused)]
fn main() {
fn validate(&self) -> Result<()> {
    check_condition_1()?;
    check_condition_2()?;
    check_condition_3()?;
    Ok(())
}
}

Sequential validation with early returns. Score: 75% confidence

Pattern 4: Builder Setters

#![allow(unused)]
fn main() {
pub fn with_field(mut self, value: T) -> Self {
    self.field = value;
    self
}
}

Simple field assignment with fluent return. Score: 95% confidence

See src/debt/boilerplate/pattern_detector.rs:18-82 for pattern detection logic.

Phase 3: Macro Analysis

Detects macro-generated code and provides recommendations:

Derivable Traits: Debtmap suggests using #[derive(...)] when it detects manual implementations of:

  • Clone, Copy, Debug, Default
  • Eq, PartialEq, Ord, PartialOrd
  • Hash

Custom Macros: Recommends creating custom derive macros for:

  • Repeated builder pattern implementations
  • Repeated conversion trait implementations
  • Repeated validation logic

Existing Crates: Suggests established crates for common patterns:

  • derive_more - Extended derive macros
  • thiserror - Error type boilerplate
  • typed-builder - Builder pattern macros
  • delegate - Delegation patterns

See src/debt/boilerplate/macro_recommender.rs:9-136 for macro recommendation logic.

Common Boilerplate Patterns

Type Conversions

#![allow(unused)]
fn main() {
// High complexity (15+), but boilerplate
impl From<ConfigFormat> for Config {
    fn from(format: ConfigFormat) -> Config {
        match format {
            ConfigFormat::Json => Config::json(),
            ConfigFormat::Yaml => Config::yaml(),
            ConfigFormat::Toml => Config::toml(),
            // ... many variants
        }
    }
}
}

Boilerplate Confidence: 90%+ Recommendation: Consider using a macro if pattern repeats

Error Propagation

#![allow(unused)]
fn main() {
// High nesting, but boilerplate pattern
fn complex_operation(&self) -> Result<Output> {
    let step1 = self.step_one()
        .context("Step one failed")?;
    let step2 = self.step_two(&step1)
        .context("Step two failed")?;
    let step3 = self.step_three(&step2)
        .context("Step three failed")?;
    Ok(Output::new(step3))
}
}

Boilerplate Confidence: 75% Recommendation: Acceptable pattern for error handling

Builder Patterns

#![allow(unused)]
fn main() {
// Many methods, but all boilerplate
impl ConfigBuilder {
    pub fn with_timeout(mut self, timeout: Duration) -> Self {
        self.timeout = Some(timeout);
        self
    }

    pub fn with_retries(mut self, retries: u32) -> Self {
        self.retries = Some(retries);
        self
    }

    // ... 20+ more setters
}
}

Boilerplate Confidence: 95% Recommendation: Use typed-builder or similar crate

Display Formatting

#![allow(unused)]
fn main() {
// High complexity due to match, but boilerplate
impl Display for Status {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match self {
            Status::Pending => write!(f, "pending"),
            Status::Running => write!(f, "running"),
            Status::Success => write!(f, "success"),
            Status::Failed(err) => write!(f, "failed: {}", err),
            // ... many variants
        }
    }
}
}

Boilerplate Confidence: 90% Recommendation: Consider using strum or derive_more

Decision Table

Use this table to interpret boilerplate confidence scores:

ConfidenceInterpretationAction
90-100%Definite boilerplateExclude from complexity prioritization; consider macro optimization
70-89%Probable boilerplateReview pattern; likely acceptable; low refactoring priority
50-69%Mixed boilerplate/logicInvestigate; may contain hidden complexity; medium priority
30-49%Mostly real complexityStandard complexity analysis; normal refactoring priority
0-29%True complexityHigh priority; focus refactoring efforts here

Example Classifications

Boilerplate (90%+ confidence):

#![allow(unused)]
fn main() {
// Simple trait delegation - skip in debt analysis
impl AsRef<str> for CustomString {
    fn as_ref(&self) -> &str {
        &self.inner
    }
}
}

Mixed (50-70% confidence):

#![allow(unused)]
fn main() {
// Match with some logic - review case by case
fn process_event(&mut self, event: Event) -> Result<()> {
    match event {
        Event::Simple => self.handle_simple(),  // Boilerplate
        Event::Complex(data) => {                // Real logic
            if data.priority > 10 {
                self.handle_urgent(data)?;
            } else {
                self.queue_normal(data)?;
            }
            self.update_metrics()?;
            Ok(())
        }
    }
}
}

True Complexity (0-30% confidence):

#![allow(unused)]
fn main() {
// Business logic requiring domain knowledge
fn calculate_optimal_strategy(&self, market: &Market) -> Strategy {
    let volatility = market.calculate_volatility();
    let trend = market.detect_trend();

    if volatility > self.risk_threshold {
        if trend.is_bullish() && self.can_hedge() {
            Strategy::hedged_long(self.calculate_position_size())
        } else {
            Strategy::defensive()
        }
    } else {
        Strategy::momentum_based(trend, self.confidence_level())
    }
}
}

Integration with Complexity Analysis

Boilerplate Scoring

Debtmap calculates a BoilerplateScore for each function:

#![allow(unused)]
fn main() {
pub struct BoilerplateScore {
    pub confidence: f64,              // 0.0-1.0 (0% to 100%)
    pub primary_pattern: Pattern,     // Strongest detected pattern
    pub contributing_patterns: Vec<Pattern>,
    pub macro_recommendation: Option<MacroRecommendation>,
}
}

Complexity Adjustment

High-confidence boilerplate reduces effective complexity:

effective_complexity = raw_complexity × (1.0 - boilerplate_confidence)

Example:

  • Raw cyclomatic complexity: 15
  • Boilerplate confidence: 0.90 (90%)
  • Effective complexity: 15 × (1.0 - 0.90) = 1.5

This prevents boilerplate from dominating debt prioritization.

Output Display

Debtmap annotates boilerplate functions in analysis output:

src/types/conversions.rs:
  ├─ from (complexity: 15, boilerplate: 92%)
  │    Pattern: Trait Implementation (From)
  │    Recommendation: Consider #[derive(From)] via derive_more
  │    Priority: Low (boilerplate)

  ├─ process_request (complexity: 12, boilerplate: 15%)
  │    Priority: High (true complexity)

Best Practices

When to Accept Boilerplate

Accept high-complexity boilerplate when:

  1. Required by language - Trait implementations, type conversions
  2. Pattern is clear - Developers can scan quickly without deep analysis
  3. Covered by tests - Mechanical patterns verified by unit tests
  4. No simpler alternative - Refactoring would reduce clarity

Example: Exhaustive match arms for enum variants with simple delegation.

When to Refactor Boilerplate

Refactor boilerplate when:

  1. Pattern repeats extensively - 10+ similar implementations
  2. Macro alternative exists - Can use derive or custom macro
  3. Maintenance burden - Changes require updating many copies
  4. Error-prone - Manual pattern increases bug risk

Example: 50+ builder setters that could use typed-builder crate.

Configuring Thresholds

Adjust boilerplate sensitivity in .debtmap.toml:

[boilerplate_detection]
enabled = true
min_confidence_to_exclude = 0.85  # Only exclude 85%+ confidence
trait_delegation_threshold = 0.90  # Trait impl confidence
pattern_match_threshold = 0.75     # Match pattern confidence

Strict mode (minimize false negatives):

min_confidence_to_exclude = 0.95  # Very high bar for exclusion

Lenient mode (minimize false positives):

min_confidence_to_exclude = 0.70  # More aggressive exclusion

Validation and Testing

Integration Test Example

Debtmap’s test suite includes real-world boilerplate validation:

#![allow(unused)]
fn main() {
#[test]
fn test_ripgrep_defs_boilerplate() {
    let code = r#"
        impl From<HyperlinkFormat> for ColorHyperlink {
            fn from(format: HyperlinkFormat) -> ColorHyperlink {
                match format {
                    HyperlinkFormat::Default => ColorHyperlink::default(),
                    // ... 15 variants
                }
            }
        }
    "#;

    let result = analyze_boilerplate(code);
    assert!(result.confidence >= 0.85, "Should detect trait boilerplate");
    assert_eq!(result.primary_pattern, Pattern::TraitImplementation);
}
}

See tests/boilerplate_integration_test.rs for complete test cases.

Performance Overhead

Boilerplate detection adds minimal overhead:

Measurement: <5% increase in analysis time Reason: Single-pass AST analysis with cached pattern matching Optimization: Trait analysis uses fast HashMap lookups

See tests/boilerplate_performance_test.rs for benchmark details.

Troubleshooting

“Why is my code marked as boilerplate?”

Check:

  1. Is it a trait implementation? (From, Display, etc.)
  2. Does it follow a mechanical pattern?
  3. Are all branches simple delegations?

If incorrectly classified:

  • Adjust min_confidence_to_exclude threshold
  • Report false positive if confidence is very high

“My boilerplate isn’t detected”

Common causes:

  1. Custom logic mixed with boilerplate pattern
  2. Non-standard trait names
  3. Complex match arm logic

Solutions:

  • Extract pure boilerplate into separate functions
  • Use standard traits when possible
  • Check confidence score - may be detected with lower confidence

“Boilerplate detection seems too aggressive”

Adjust configuration:

[boilerplate_detection]
min_confidence_to_exclude = 0.95  # Raise threshold
trait_delegation_threshold = 0.95

Summary

Boilerplate detection is a critical feature that:

  • Distinguishes mechanical patterns from true complexity
  • Reduces false positives in debt analysis
  • Provides actionable macro recommendations
  • Integrates seamlessly with complexity scoring
  • Helps teams focus on real maintainability issues

By identifying boilerplate with 85%+ confidence, Debtmap ensures that high-complexity scores reflect actual cognitive burden rather than necessary but mechanical code patterns.