Architectural Analysis
Debtmap provides comprehensive architectural analysis capabilities based on Robert C. Martin’s software engineering principles. These tools help identify structural issues, coupling problems, and architectural anti-patterns in your codebase.
Overview
Architectural analysis examines module-level relationships and dependencies to identify:
- Circular Dependencies - Modules that create dependency cycles
- Coupling Metrics - Afferent and efferent coupling measurements
- Bidirectional Dependencies - Inappropriate intimacy between modules
- Stable Dependencies Principle Violations - Unstable modules being depended upon
- Zone of Pain - Rigid, concrete implementations heavily depended upon
- Zone of Uselessness - Overly abstract, unstable modules
- Code Duplication - Identical or similar code blocks across files
These analyses help you maintain clean architecture and identify refactoring opportunities.
Circular Dependency Detection
Circular dependencies occur when modules form a dependency cycle (A depends on B, B depends on C, C depends on A). These violations break architectural boundaries and make code harder to understand, test, and maintain.
How It Works
Debtmap builds a dependency graph from module imports and uses depth-first search (DFS) with recursion stack tracking to detect cycles:
- Parse all files to extract import/module dependencies
- Build a directed graph where nodes are modules and edges are dependencies
- Run DFS from each unvisited module
- Track visited nodes and recursion stack
- When a node is reached that’s already in the recursion stack, a cycle is detected
Implementation: src/debt/circular.rs:44-66 (detect_circular_dependencies)
Example
#![allow(unused)] fn main() { // Module A (src/auth.rs) use crate::user::User; use crate::session::validate_session; // Module B (src/user.rs) use crate::session::Session; // Module C (src/session.rs) use crate::auth::authenticate; // Creates cycle: auth → session → auth }
Debtmap detects:
Circular dependency detected: auth → session → auth
Refactoring Recommendations
To break circular dependencies:
- Extract Interface - Create a trait that both modules depend on
- Dependency Inversion - Introduce an abstraction layer
- Move Shared Code - Extract common functionality to a new module
- Remove Dependency - Inline or duplicate small amounts of code
Coupling Metrics
Coupling metrics measure how interconnected modules are. Debtmap calculates two primary metrics:
Afferent Coupling (Ca)
Afferent coupling is the number of modules that depend on this module. High afferent coupling means many modules rely on this code.
#![allow(unused)] fn main() { pub struct CouplingMetrics { pub module: String, pub afferent_coupling: usize, // Number depending on this module pub efferent_coupling: usize, // Number this module depends on pub instability: f64, // Calculated from Ca and Ce pub abstractness: f64, // Ratio of abstract types } }
Implementation: src/debt/coupling.rs:6-30
Efferent Coupling (Ce)
Efferent coupling is the number of modules this module depends on. High efferent coupling means this module has many dependencies.
Example Coupling Analysis
Module: api_handler
Afferent coupling (Ca): 8 // 8 modules depend on api_handler
Efferent coupling (Ce): 3 // api_handler depends on 3 modules
Instability: 0.27 // Relatively stable
High afferent or efferent coupling (typically >5) indicates potential maintainability issues.
Instability Metric
The instability metric measures how resistant a module is to change. It’s calculated as:
I = Ce / (Ca + Ce)
Interpretation:
- I = 0.0 - Maximally stable (no dependencies, many dependents)
- I = 1.0 - Maximally unstable (many dependencies, no dependents)
Implementation: src/debt/coupling.rs:16-24 (calculate_instability)
Stability Guidelines
- Stable modules (I < 0.3) - Hard to change but depended upon; should contain stable abstractions
- Balanced modules (0.3 ≤ I ≤ 0.7) - Normal modules with both dependencies and dependents
- Unstable modules (I > 0.7) - Change frequently; should have few or no dependents
Example
#![allow(unused)] fn main() { // Stable module (I = 0.1) // core/types.rs - defines fundamental types, depended on by 20 modules pub struct User { ... } pub struct Session { ... } // Unstable module (I = 0.9) // handlers/admin_dashboard.rs - depends on 10 modules, no dependents use crate::auth::*; use crate::database::*; use crate::templates::*; // ... 7 more imports }
Stable Dependencies Principle
The Stable Dependencies Principle (SDP) states: Depend in the direction of stability. Modules should depend on modules that are more stable than themselves.
SDP Violations
Debtmap flags violations when a module has:
- Instability > 0.8 (very unstable)
- Afferent coupling > 2 (multiple modules depend on it)
This means an unstable, frequently changing module is being depended upon by multiple other modules - a recipe for maintenance problems.
Implementation: src/debt/coupling.rs:69-76
Example Violation
Module 'temp_utils' violates Stable Dependencies Principle
(instability: 0.85, depended on by 5 modules)
Problem: This module changes frequently but is heavily depended upon.
Solution: Extract stable interface or reduce dependencies on this module.
Fixing SDP Violations
- Increase stability - Reduce the module’s dependencies
- Reduce afferent coupling - Extract interface, use dependency injection
- Split module - Separate stable and unstable parts
Bidirectional Dependencies
Bidirectional dependencies (also called inappropriate intimacy) occur when two modules depend on each other:
Module A depends on Module B
Module B depends on Module A
This creates tight coupling and makes both modules harder to change, test, or reuse independently.
Implementation: src/debt/coupling.rs:98-117 (detect_inappropriate_intimacy)
Example
#![allow(unused)] fn main() { // order.rs use crate::customer::Customer; pub struct Order { customer: Customer, } // customer.rs use crate::order::Order; // Bidirectional dependency! pub struct Customer { orders: Vec<Order>, } }
Debtmap detects:
Inappropriate intimacy detected between 'order' and 'customer'
Refactoring Recommendations
- Create Mediator - Introduce a third module to manage the relationship
- Break into Separate Modules - Split concerns more clearly
- Use Events - Replace direct dependencies with event-driven communication
- Dependency Inversion - Introduce interfaces/traits both depend on
Zone of Pain Detection
The zone of pain contains modules with:
- Low abstractness (< 0.2) - Concrete implementations, no abstractions
- Low instability (< 0.2) - Stable, hard to change
- High afferent coupling (> 3) - Many modules depend on them
These modules are rigid concrete implementations that are heavily used but hard to change - causing pain when modifications are needed.
Implementation: src/debt/coupling.rs:125-138
Example
Module 'database_client' is in the zone of pain (rigid and hard to change)
Abstractness: 0.1 (all concrete implementation)
Instability: 0.15 (very stable, many dependents)
Afferent coupling: 12 (12 modules depend on it)
Problem: This concrete database client is used everywhere.
Any change to its implementation requires updating many modules.
Refactoring Recommendations
- Extract Interfaces - Create a
DatabaseClienttrait - Introduce Abstractions - Define abstract operations others depend on
- Break into Smaller Modules - Separate concerns to reduce coupling
- Use Dependency Injection - Pass implementations via interfaces
Zone of Uselessness Detection
The zone of uselessness contains modules with:
- High abstractness (> 0.8) - Mostly abstract, few concrete implementations
- High instability (> 0.8) - Frequently changing
These modules are overly abstract and unstable, providing little stable value to the system.
Implementation: src/debt/coupling.rs:141-153
Example
Module 'base_processor' is in the zone of uselessness
(too abstract and unstable)
Abstractness: 0.9 (mostly traits and interfaces)
Instability: 0.85 (changes frequently)
Problem: This module defines many abstractions but provides little
concrete value. It changes often, breaking implementations.
Refactoring Recommendations
- Add Concrete Implementations - Make the module useful by implementing functionality
- Remove if Unused - Delete if no real value is provided
- Stabilize Interfaces - Stop changing abstractions frequently
- Merge with Implementations - Combine abstract and concrete code
Distance from Main Sequence
The main sequence represents the ideal balance between abstractness and instability. Modules should lie on the line:
A + I = 1
Where:
- A = Abstractness (ratio of abstract types to total types)
- I = Instability (Ce / (Ca + Ce))
Distance from the main sequence:
D = |A + I - 1|
Implementation: src/debt/coupling.rs:119-123
Interpretation
- D ≈ 0.0 - Module is on the main sequence (ideal)
- D > 0.5 - Module is far from ideal
- High D with low A and I → Zone of Pain
- High D with high A and I → Zone of Uselessness
Visual Representation
Abstractness
1.0 ┤ Zone of Uselessness
│ ╱
│ ╱
0.5 ┤ ╱ Main Sequence
│╱
╱
0.0 ┤──────────────────────────
0.0 0.5 1.0
Instability
Zone of Pain
Code Duplication Detection
Debtmap detects code duplication using hash-based chunk comparison:
- Extract chunks - Split files into fixed-size chunks (default: 50 lines)
- Normalize - Remove whitespace and comments
- Calculate hash - Compute SHA-256 hash for each normalized chunk
- Match duplicates - Find chunks with identical hashes
- Merge adjacent - Consolidate consecutive duplicate blocks
Implementation: src/debt/duplication.rs:6-44 (detect_duplication)
Algorithm Details
#![allow(unused)] fn main() { pub fn detect_duplication( files: Vec<(PathBuf, String)>, min_lines: usize, // Default: 50 _similarity_threshold: f64, // Currently unused (exact matching) ) -> Vec<DuplicationBlock> }
The algorithm:
- Extracts overlapping chunks from each file
- Normalizes by trimming whitespace and removing comments
- Calculates SHA-256 hash for each normalized chunk
- Groups chunks by hash
- Returns groups with 2+ locations (duplicates found)
Example Output
Code duplication detected:
Hash: a3f2b9c1...
Lines: 50
Locations:
- src/handlers/user.rs:120-169
- src/handlers/admin.rs:85-134
- src/handlers/guest.rs:200-249
Recommendation: Extract common validation logic to shared module
Duplication Configuration
Configure duplication detection in .debtmap.toml:
# Minimum lines for duplication detection
threshold_duplication = 50 # Default value
# Smaller values catch more duplications but increase noise
# threshold_duplication = 30 # More sensitive
# Larger values only catch major duplications
# threshold_duplication = 100 # Less noise
Configuration reference: features.json:21 (threshold_duplication)
Implementation: src/debt/duplication.rs:6-10
Current Limitations
- Exact matching only - Currently uses hash-based exact matching
- similarity_threshold parameter - Defined but not implemented yet
- Future enhancement - Fuzzy matching for near-duplicates
Refactoring Recommendations
Debtmap provides specific refactoring recommendations for each architectural issue:
For Circular Dependencies
- Extract Interface - Create shared abstraction both modules use
- Dependency Inversion - Introduce interfaces to reverse dependency direction
- Move Shared Code - Extract to new module both can depend on
- Event-Driven - Replace direct calls with event publishing/subscribing
For High Coupling
- Facade Pattern - Provide simplified interface hiding complex dependencies
- Reduce Dependencies - Remove unnecessary imports and calls
- Dependency Injection - Pass dependencies via constructors/parameters
- Interface Segregation - Split large interfaces into focused ones
For Zone of Pain
- Introduce Abstractions - Extract traits/interfaces for flexibility
- Adapter Pattern - Wrap concrete implementations with adapters
- Strategy Pattern - Make algorithms pluggable via interfaces
For Zone of Uselessness
- Add Concrete Implementations - Provide useful functionality
- Remove Unused Code - Delete if providing no value
- Stabilize Interfaces - Stop changing abstractions frequently
For Bidirectional Dependencies
- Create Mediator - Third module manages relationship
- Break into Separate Modules - Clearer separation of concerns
- Observer Pattern - One-way communication via observers
For Code Duplication
- Extract Common Code - Create shared function/module
- Use Inheritance/Composition - Share via traits or composition
- Parameterize Differences - Extract variable parts as parameters
- Template Method - Define algorithm structure, vary specific steps
Examples and Use Cases
Running Architectural Analysis
# Full analysis with all architectural checks
debtmap analyze --include-architecture
# Focus on circular dependencies
debtmap analyze --check circular-deps
# Coupling analysis with custom threshold
debtmap analyze --coupling-threshold 5
# Duplication detection with custom chunk size
debtmap analyze --duplication-min-lines 30
Example: Circular Dependency
Before:
src/auth.rs → src/session.rs → src/user.rs → src/auth.rs
Circular dependency detected: auth → session → user → auth
After refactoring:
src/auth.rs → src/auth_interface.rs ← src/session.rs
↑
src/user.rs
No circular dependencies found.
Example: Coupling Metrics Table
Module Analysis Results:
Module Ca Ce Instability Issues
-------------------------------------------------
core/types 15 0 0.00 None
api/handlers 2 8 0.80 High Ce
database/client 8 2 0.20 None
utils/temp 5 12 0.71 SDP violation
auth/session 3 3 0.50 None
Example: Zone of Pain
Module: legacy_db_client
Metrics:
Abstractness: 0.05 (all concrete code)
Instability: 0.12 (depended on by 25 modules)
Afferent coupling: 25
Distance from main sequence: 0.83
Status: Zone of Pain - rigid and hard to change
Refactoring steps:
1. Extract interface DatabaseClient trait
2. Create adapter wrapping legacy implementation
3. Gradually migrate dependents to use trait
4. Introduce alternative implementations
Interpreting Results
Prioritization
Address architectural issues in this order:
-
Circular Dependencies (Highest Priority)
- Break architectural boundaries
- Make testing impossible
- Cause build issues
-
Bidirectional Dependencies (High Priority)
- Create tight coupling
- Prevent independent testing
- Block modular changes
-
Zone of Pain Issues (Medium-High Priority)
- Indicate rigid architecture
- Block future changes
- High risk for bugs
-
SDP Violations (Medium Priority)
- Cause ripple effects
- Increase maintenance cost
- Unstable foundation
-
High Coupling (Medium Priority)
- Maintainability risk
- Testing difficulty
- Change amplification
-
Code Duplication (Lower Priority)
- Maintenance burden
- Bug multiplication
- Inconsistency risk
Decision Flowchart
Is there a circular dependency?
├─ YES → Break immediately (extract interface, DI)
└─ NO → Continue
Is there bidirectional dependency?
├─ YES → Refactor (mediator, event-driven)
└─ NO → Continue
Is module in zone of pain?
├─ YES → Introduce abstractions
└─ NO → Continue
Is SDP violated?
├─ YES → Stabilize or reduce afferent coupling
└─ NO → Continue
Is coupling > threshold?
├─ YES → Reduce dependencies
└─ NO → Continue
Is there significant duplication?
├─ YES → Extract common code
└─ NO → Architecture is good!
Integration with Debt Categories
Architectural analysis results are integrated with debtmap’s debt categorization system:
Debt Type Mapping
- CircularDependency - Circular dependency cycles detected
- HighCoupling - Modules exceeding coupling threshold
- Duplication - Duplicated code blocks found
- ArchitecturalViolation - SDP violations, zone issues
Reference: features.json:240-253 (core_patterns section)
Tiered Prioritization
Architectural issues are assigned priority tiers:
- Tier 1 (Critical) - Circular dependencies, bidirectional dependencies
- Tier 2 (High) - Zone of pain, SDP violations
- Tier 3 (Medium) - High coupling, large duplications
- Tier 4 (Low) - Small duplications, minor coupling issues
Reference: features.json:522-560 (tiered prioritization)
Cohesion Analysis
Note: Module cohesion analysis is currently a simplified placeholder implementation.
Current status: src/debt/coupling.rs:82-95 (analyze_module_cohesion)
The function exists but provides basic cohesion calculation. Full cohesion analysis (measuring how well module elements belong together) is planned for a future release.
Future Enhancement
Full cohesion analysis would measure:
- Functional cohesion (functions operating on related data)
- Sequential cohesion (output of one function feeds another)
- Communicational cohesion (functions operating on same data structures)
Configuration
Configure architectural analysis thresholds in .debtmap.toml:
[architectural]
# Coupling threshold (modules with more dependencies flagged)
coupling_threshold = 5
# Minimum lines for duplication detection
duplication_min_lines = 50
# Instability threshold for SDP violations
instability_threshold = 0.8
# Minimum afferent coupling for SDP violations
sdp_afferent_threshold = 2
# Zone of pain thresholds
zone_pain_abstractness = 0.2
zone_pain_instability = 0.2
zone_pain_afferent = 3
# Zone of uselessness thresholds
zone_useless_abstractness = 0.8
zone_useless_instability = 0.8
See Configuration for complete options.
Troubleshooting
“No circular dependencies detected but build fails”
Cause: Circular dependencies at the package/crate level, not module level.
Solution: Use cargo tree to analyze package-level dependencies.
“Too many coupling warnings”
Cause: Default threshold may be too strict for your codebase.
Solution: Adjust coupling_threshold in .debtmap.toml to match your architecture.
“Duplication detected in generated code”
Cause: Code generation tools create similar patterns.
Solution: Use suppression patterns to exclude generated files. See Suppression Patterns.
“Zone of pain false positives”
Cause: Utility modules are intentionally stable and concrete.
Solution: This is often correct - utility modules should be stable. Consider whether the module should be more abstract.
Further Reading
Robert C. Martin’s Principles
The architectural metrics in debtmap are based on:
- Clean Architecture by Robert C. Martin
- Agile Software Development: Principles, Patterns, and Practices by Robert C. Martin
- Stable Dependencies Principle (SDP)
- Stable Abstractions Principle (SAP)
- Main Sequence distance metric
Related Topics
- Analysis Guide - Complete analysis workflow
- Configuration - Configuration options
- Entropy Analysis - Complexity vs. entropy
- Scoring Strategies - How debt is scored
- Tiered Prioritization - Priority assignment