cargo-coupling
May 27, 2026 · View on GitHub
Measure the "right distance" in your Rust code.
cargo-coupling analyzes coupling in Rust projects based on Vlad Khononov's "Balancing Coupling in Software Design" framework. It calculates a Balance Score from three core dimensions: Integration Strength, Distance, and Volatility.

⚠️ Experimental Project
This tool is currently experimental. The scoring algorithms, thresholds, and detected patterns are subject to change based on real-world feedback.
We want your input! If you try this tool on your project, please share your experience:
- Are the grades and scores meaningful for your codebase?
- Are there false positives or patterns that shouldn't be flagged?
- What additional metrics would be useful?
Please open an issue at GitHub Issues to discuss. Your feedback helps improve the tool for everyone.
Quick Start
1. Install
cargo install cargo-coupling
Or use Docker:
docker pull ghcr.io/nwiizo/cargo-coupling
2. Analyze
# Analyze current project (default: shows only important issues)
cargo coupling ./src
# Show summary only
cargo coupling --summary ./src
# Japanese output with explanations (日本語出力)
cargo coupling --summary --japanese ./src
cargo coupling --summary --jp ./src
# Show all issues including Low severity
cargo coupling --summary --all ./src
3. Track Coupling Health Over Time
# Show coupling health across recent git revisions
cargo coupling --history ./src
# Use 8 sampled revisions and a 12-month git window
cargo coupling --history=8 --git-months=12 ./src
# Machine-readable history timeline
cargo coupling --history=8 --json ./src
--history re-analyzes sampled git revisions in disposable worktrees. Output is chronological and uses the same snapshot methodology as normal analysis. The web UI also exposes this data as a timeline with auto-play.
4. Compare Against a Baseline
# Diff current issues against a git ref
cargo coupling --baseline main ./src
# Ratchet gate: fail only on NEW High/Critical issues
cargo coupling --check --baseline main ./src
# Ratchet on Medium or higher
cargo coupling --check --baseline main --fail-on=medium ./src
Baseline diffs use (issue_type, source, target) as the stable issue key. --check --baseline <ref> is useful in CI because existing debt does not fail the build; only new issues at the configured severity do.
5. Review Blind Spots
# Default output shows run notes and a pointer to the full manifest
cargo coupling ./src
# Show the full "Not Analyzed" declaration
cargo coupling --blind-spots ./src
# --all also expands the blind-spot list
cargo coupling --all ./src
# JSON and AI output always include the full manifest
cargo coupling --json ./src
cargo coupling --ai ./src
The blind-spot manifest declares what the analyzer did not observe, such as runtime connascence, organizational/runtime distance, duplicated logic that does not co-change, and macro or inactive cfg paths. Treat a clean report as "no observed issues", not proof that no coupling risk exists.
6. Refactor with AI
# Generate AI-friendly output
cargo coupling --ai ./src
Copy the output and use this prompt with Claude, Copilot, or any AI coding assistant:
The following is the output of `cargo coupling --ai`, which analyzes coupling issues in a Rust project.
For each issue, suggest specific code changes to reduce coupling.
Focus on introducing traits, moving code closer, or breaking circular dependencies.
Example output:
Coupling Issues in my-project:
────────────────────────────────────────────────────────────
Grade: B (Good) | Score: 0.88 | Issues: 0 High, 5 Medium
Issues:
1. 🟡 api::handler → db::internal::Query
Type: Global Complexity
Problem: Intrusive coupling to db::internal::Query across module boundary
Fix: Introduce trait `QueryTrait` with methods: // Extract required methods
2. 🟡 25 dependents → core::types
Type: High Afferent Coupling
Problem: Module core::types is depended on by 25 other components
Fix: Introduce trait `TypesInterface` with methods: // Define stable public API
The AI will analyze patterns and suggest specific refactoring strategies.
7. Interactive Web Visualization (Experimental)
⚠️ Experimental Feature: The Web UI is currently in an experimental state. The interface, features, and behavior may change significantly in future versions.

# Start interactive web UI
cargo coupling --web ./src
# Custom port
cargo coupling --web --port 8080 ./src
The web UI provides:
- Interactive 2D and 3D coupling graph views
- Dimension-Space exploration for strength, distance, volatility, and balance
- Timeline view for
--historydata with auto-play - Trust panels that expose analysis confidence, run notes, and declared blind spots
- Hotspots panel: Top refactoring targets ranked by severity
- Blast Radius: Impact analysis with risk score
- Clusters: Architecture grouping detection
- Filtering by strength, distance, volatility, balance score
- Source code viewing with syntax highlighting
8. Job-Focused CLI Commands
For quick, focused analysis without opening the web UI:
# Find top refactoring targets
cargo coupling --hotspots ./src
cargo coupling --hotspots=10 ./src
# With beginner-friendly explanations
cargo coupling --hotspots --verbose ./src
# Analyze change impact for a specific module
cargo coupling --impact main ./src
cargo coupling --impact analyzer ./src
# Trace dependencies for a specific function or type
cargo coupling --trace analyze_file ./src
cargo coupling --trace BalanceScore ./src
# CI/CD quality gate (exits with code 1 on failure)
cargo coupling --check ./src
cargo coupling --check --min-grade=B ./src
cargo coupling --check --max-critical=0 --max-circular=0 ./src
# Machine-readable JSON output
cargo coupling --json ./src
cargo coupling --json ./src | jq '.hotspots[0]'
Example --hotspots --verbose output:
#1 my-project::main (Score: 55)
🟡 Medium: High Efferent Coupling
💡 What it means:
This module depends on too many other modules
⚠️ Why it's a problem:
• Changes elsewhere may break this module
• Testing requires many mocks/stubs
• Hard to understand in isolation
🔧 How to fix:
Split into smaller modules with clear responsibilities
e.g., Split main.rs into cli.rs, config.rs, runner.rs
More Options
# Generate detailed report to file
cargo coupling -o report.md ./src
# Show timing information
cargo coupling --summary --timing ./src
# Use 4 threads for parallel processing
cargo coupling -j 4 ./src
# Skip Git history analysis for faster results
cargo coupling --no-git ./src
Features
- 3-Dimensional Balance Score: Calculates coupling balance based on Integration Strength, Distance, and Volatility (0.0 - 1.0)
- Khononov Balance Formula:
BALANCE = (STRENGTH XOR DISTANCE) OR NOT VOLATILITY - Interactive Web UI:
--webstarts browser-based 2D/3D graph, Dimension-Space, timeline, trust, hotspot, and blast-radius views - Job-Focused CLI: Quick commands for common tasks (
--hotspots,--impact,--check,--json) - Japanese Support:
--japanese/--jpflag for Japanese output with explanations and design decision matrix - Noise Reduction: Default strict mode hides Low severity issues (
--allto show all) - Blind-Spot Manifest: Declares what was not analyzed; text can expand it with
--blind-spots, and JSON/AI output includes it by default - Beginner-Friendly:
--verboseflag explains issues in plain language with fix examples - CI/CD Quality Gate:
--checkcommand with configurable thresholds and exit codes - Baseline Ratchet Gate:
--baseline <ref>diffs issues;--check --baseline <ref>fails only on new issues - AI-Friendly Output:
--aiflag generates output optimized for coding agents (Claude, Copilot, etc.) - Rust Pattern Detection: Detects newtype usage, serde derives, public fields, primitive obsession
- Issue Detection: Automatically identifies problematic coupling patterns, including Hidden Coupling and Accidental Volatility
- Circular Dependency Detection: Detects and reports dependency cycles
- Visibility Tracking: Analyzes Rust visibility modifiers (pub, pub(crate), etc.)
- Git Integration: Analyzes change frequency from Git history for volatility scoring
- History Timeline:
--history[=N]samples git revisions via worktrees for time-series coupling health - DDD Subdomain Classification: Configure core/supporting/generic subdomains to model business-driven volatility
- Temporal Coupling Detection: Detects files that frequently co-change in Git history (implicit coupling)
- Configuration File: Supports
.coupling.tomlfor analysis excludes, volatility overrides, and thresholds - Parallel Processing: Uses Rayon for fast analysis of large codebases
- Configurable Thresholds: Customize dependency limits via CLI or config
- Markdown Reports: Generates detailed analysis reports
- Cargo Integration: Works as a cargo subcommand
Configuration
Use .coupling.toml to customize analysis behavior:
[analysis]
exclude_tests = true
prelude_modules = ["src/lib.rs", "src/prelude.rs"]
exclude = ["src/generated/*", "src/generated/**"]
[volatility]
high = ["src/application/*"]
low = ["src/domain/*"]
[subdomains]
# Core subdomains are expected to have essential volatility.
core = ["src/balance.rs", "src/metrics.rs"]
# Supporting and generic subdomains should normally be stable.
supporting = ["src/analyzer.rs", "src/report.rs", "src/cli_output.rs"]
generic = ["src/config.rs", "src/workspace.rs", "src/web/**"]
[thresholds]
max_dependencies = 15
max_dependents = 20
cargo-coupling searches for .coupling.toml (or coupling.toml) from the analysis path upward. [analysis].exclude patterns are evaluated relative to the directory that contains the config file, so when you analyze ./src, write patterns like src/generated/**, not just generated/**.
[subdomains] classifies modules as DDD core/supporting/generic. Core modules are expected to change as the product model evolves; supporting and generic modules should usually be stable. High churn in supporting/generic modules is reported as Accidental Volatility because Khononov's model distinguishes essential business volatility from churn caused by design or implementation friction. This repository's own .coupling.toml is a concrete example.
Use --config <PATH> when you want to load a specific config file instead of relying on auto-discovery.
Khononov's Coupling Balance
Coupling Balance is a framework proposed by Vlad Khononov that evaluates coupling between modules across three dimensions to guide design decisions.
Core Principle
Coupling is not inherently bad. What matters is the balance between coupling strength, distance, and volatility.
The Three Dimensions
1. Strength (Integration Strength)
Represents how tightly components depend on each other.
| Level | Description | Rust Example | Score |
|---|---|---|---|
| Intrusive | Direct dependency on internal implementation | Direct access to struct.field | 1.00 (strong) |
| Functional | Dependency on behavior | Method calls on concrete types | 0.75 |
| Model | Dependency on data structures | Sharing type definitions | 0.50 |
| Contract | Dependency on interfaces only | Access via trait | 0.25 (weak) |
→ Lower in the table = weaker coupling (preferred)
2. Distance
The physical or logical distance between dependent components.
| Level | Description | Score |
|---|---|---|
| Same Module | Within the same module | 0.25 (close) |
| Different Module | Different module in the same crate | 0.50 |
| External Crate | Dependency on external crate | 1.00 (far) |
→ Lower in the table = farther distance
3. Volatility
How frequently a component changes. This is calculated from Git history and can be informed by DDD subdomain classification in .coupling.toml.
| Level | Description | Git signal | Subdomain signal | Score |
|---|---|---|---|---|
| Low | Stable, rarely changes | 0-2 changes | Supporting, Generic | 0.00 |
| Medium | Occasionally changes | 3-10 changes | - | 0.50 |
| High | Frequently changes | 11+ changes | Core | 1.00 |
Note: Volatility requires Git history. Use
cargo coupling ./src(not--no-git) to enable volatility analysis.
Khononov's rationale is that volatility is not always bad. Core subdomains have essential volatility because they contain the evolving business model. Supporting and generic subdomains should be stable; if they churn heavily, the tool reports Accidental Volatility.
The Balance Law
Good design follows this principle:
Strong coupling is only acceptable when distance is close OR volatility is low
Expressed as a logical formula:
BALANCED = (STRENGTH ≤ threshold) OR (DISTANCE = near) OR (VOLATILITY = low)
Or Khononov's formula:
BALANCE = (STRENGTH XOR DISTANCE) OR NOT VOLATILITY
``$
- **\text{STRENGTH} \text{XOR} \text{DISTANCE}**: \text{Strong} \text{coupling} \times \text{close} \text{distance} \text{OR} \text{weak} \text{coupling} \times \text{far} \text{distance} = \text{Good}
- **\text{OR} \text{NOT} \text{VOLATILITY}**: \text{Even} \text{if} \text{the} \text{above} \text{isn}'\text{t} \text{satisfied}, \text{low} \text{volatility} \text{makes} \text{it} \text{acceptable}
## \text{Design} \text{Decision} \text{Matrix}
| \text{Strength} | \text{Distance} | \text{Volatility} | \text{Decision} | \text{Reason} |
|----------|----------|------------|----------|--------|
| \text{Strong} | \text{Close} | \text{Low}-\text{Medium} | ✅ \text{OK} | \text{High} \text{cohesion}, \text{changes} \text{are} \text{localized} |
| \text{Weak} | \text{Far} | \text{Any} | ✅ \text{OK} | \text{Loose} \text{coupling} \text{with} \text{healthy} \text{dependencies} |
| \text{Strong} | \text{Far} | \text{Any} | ⚠️ \text{Needs} \text{improvement} | \text{Change} \text{impact} \text{spreads} \text{widely} (\text{global} \text{complexity}) |
| \text{Strong} | \text{Any} | \text{High} | ⚠️ \text{Needs} \text{improvement} | \text{Changes} \text{cascade} \text{through} \text{the} \text{system} |
| \text{Weak} | \text{Close} | \text{Low} | 🤔 \text{Consider} | \text{Opportunity} \text{for} \text{integration} (\text{possibly} \text{over}-\text{modularized}) |
## \text{Improvement} \text{Patterns}
### \text{Pattern} 1: \text{Reducing} \text{Coupling} \text{Strength} \text{via} \text{Abstraction}
**\text{Problem}**: \text{Strong} \text{coupling} + \text{far} \text{distance}
$``
┌─────────────┐ ┌─────────────┐
│ Module A │ ──────▶ │ Module B │
│ │ strong │ (impl) │
└─────────────┘ └─────────────┘
far distance (different module)
Solution: Introduce a Contract (trait)
┌─────────────┐ ┌─────────────┐
│ Module A │ ──────▶ │ trait T │
│ │ weak │ (contract) │
└─────────────┘ └─────────────┘
▲
│ implements
┌─────────────┐
│ Module B │
│ (impl) │
└─────────────┘
Pattern 2: Isolating Volatility
Problem: Strong coupling + high volatility
Solution: Insert a stable interface layer
Concrete Example (Rust)
Before: Problematic Code
// module_a.rs
fn process_user(user: &User) {
// Direct access to struct internal fields (Intrusive)
let name = &user.name; // ← strong coupling
let age = user.age; // ← strong coupling
let email = &user.email_address; // ← breaks if field name changes
// ...
}
// module_b.rs (frequently modified)
pub struct User {
pub name: String,
pub age: u32,
pub email_address: String, // ← renamed from email
}
Issues:
- Coupling strength: Intrusive (direct field access)
- Distance: Different Module
- Volatility: High (User struct changes frequently)
After: Improved Code
// contracts.rs (stable layer)
pub trait UserInfo {
fn display_name(&self) -> &str;
fn age(&self) -> u32;
fn contact_email(&self) -> &str;
}
// module_b.rs (implementation details hidden)
pub struct User {
name: String, // changed to private
age: u32,
email_address: String,
}
impl UserInfo for User {
fn display_name(&self) -> &str { &self.name }
fn age(&self) -> u32 { self.age }
fn contact_email(&self) -> &str { &self.email_address }
}
// module_a.rs (access via trait)
fn process_user(user: &impl UserInfo) {
let name = user.display_name(); // ← Contract coupling
let age = user.age(); // ← Contract coupling
let email = user.contact_email(); // ← unaffected by internal changes
// ...
}
Improvements:
- Coupling strength: Reduced to Contract (via trait)
- Changes are contained within the
Userstruct module_ano longer needs to knowUser's internal structure
Coupling Balance Summary
| Perspective | Guideline |
|---|---|
| Strong coupling... | Keep components close, or reduce volatility |
| Far dependencies... | Use weak coupling (Contract) |
| Highly volatile components... | Isolate with stable abstraction layers |
Coupling balance is not about "eliminating coupling" but about "placing the right strength of coupling in the right place."
Numeric Implementation
In the actual implementation:
let alignment = 1.0 - (strength - (1.0 - distance)).abs();
let volatility_impact = 1.0 - (volatility * strength);
let score = alignment * volatility_impact;
CLI Options
cargo coupling [OPTIONS] [PATH]
Arguments:
[PATH] Path to analyze [default: ./src]
Options:
-o, --output <FILE> Output report to file
-s, --summary Show summary only
--ai AI-friendly output for coding agents
--all Show all issues (default: hide Low severity)
--japanese, --jp Japanese output with explanations (日本語)
--git-months <MONTHS> Git history period [default: 6]
--no-git Skip Git analysis
--exclude-tests Exclude test code from analysis
-c, --config <CONFIG> Config file path (default: search for .coupling.toml)
-v, --verbose Verbose output with explanations
--timing Show timing information
-j, --jobs <N> Number of threads (default: auto)
--max-deps <N> Max outgoing dependencies
--max-dependents <N> Max incoming dependencies
Web Visualization:
--web Start interactive web UI
--port <PORT> Web server port [default: 3000]
--no-open Don't auto-open browser
--api-endpoint <URL> API endpoint URL (for separate deployments)
Job-Focused Commands:
--hotspots[=<N>] Show top N refactoring targets [default: 5]
--impact <MODULE> Analyze change impact for a module
--trace <ITEM> Trace dependencies for a function/type
--history[=<N>] Show coupling health over git history [default: 12 samples]
--baseline <GIT_REF> Compare current issues against a baseline ref
--check CI/CD quality gate (exit code 1 on failure)
--min-grade <GRADE> Minimum grade for --check (A/B/C/D/F)
--max-critical <N> Max critical issues for --check
--max-circular <N> Max circular dependencies for --check
--fail-on <SEVERITY> Fail --check on severity (critical/high/medium/low)
--json Output in JSON format
--blind-spots Show the full structural blind-spot list in text output
-h, --help Print help
-V, --version Print version
Thresholds
Issue Detection Thresholds
The tool uses the following default thresholds for detecting coupling issues:
| Threshold | Default | CLI Flag | Description |
|---|---|---|---|
| Strong Coupling | 0.75 | - | Minimum strength value considered "strong" (Intrusive level) |
| Far Distance | 0.50 | - | Minimum distance value considered "far" (DifferentModule+) |
| High Volatility | 0.75 | - | Minimum volatility value considered "high" |
| Max Dependencies | 15 | --max-deps | Outgoing dependencies before flagging High Efferent Coupling |
| Max Dependents | 20 | --max-dependents | Incoming dependencies before flagging High Afferent Coupling |
Health Grade Calculation
Health grades are calculated based on internal couplings only (external crate dependencies are excluded):
| Grade | Description | Criteria |
|---|---|---|
| S (Over-optimized!) | Stop refactoring! | Medium density <= 5% with >= 20 couplings |
| A (Well-balanced) | Coupling is appropriate | Medium density 5-10%, no high issues |
| B (Healthy) | Minor issues, manageable | Medium density > 10%, no critical issues |
| C (Room for improvement) | Some structural issues | Any high issues OR medium density > 25% |
| D (Attention needed) | Significant issues | Any critical issues OR high density > 5% |
| F (Immediate action required) | Critical issues | More than 3 critical issues |
Note: S is a WARNING, not a reward. It means you might be over-engineering. Aim for A.
Severity Classification
Issues are classified by severity based on:
| Severity | Criteria |
|---|---|
| Critical | Multiple critical issues detected (circular dependencies, etc.) |
| High | Count > threshold × 2 (e.g., > 40 dependencies when threshold is 20) |
| Medium | Count > threshold but <= threshold × 2 |
| Low | Minor issues, generally informational |
Output Example
Summary Mode (English)
$ cargo coupling --summary ./src
Balanced Coupling Analysis: my-project
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Grade: B (Good) | Score: 0.67/1.00 | Modules: 14
3-Dimensional Analysis:
Strength: Contract 1% / Model 24% / Functional 66% / Intrusive 8%
Distance: Same 6% / Different 2% / External 91%
Volatility: Low 2% / Medium 98% / High 0%
Balance State:
✅ High Cohesion (strong+close): 24 (6%)
✅ Loose Coupling (weak+far): 5 (1%)
🤔 Acceptable (strong+far+stable): 352 (92%)
Detected Issues:
🟡 Medium: 3
Top Priorities:
- [Medium] metrics → 17 functions, 17 types, 11 impls
- [Medium] main → 21 dependencies
Summary Mode (Japanese)
$ cargo coupling --summary --jp ./src
カップリング分析: my-project
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
評価: B (Good) | スコア: 0.67/1.00 | モジュール数: 14
3次元分析:
結合強度: Contract 1% / Model 24% / Functional 66% / Intrusive 8%
(トレイト) (型) (関数) (内部アクセス)
距離: 同一モジュール 6% / 別モジュール 2% / 外部 91%
変更頻度: 低 2% / 中 98% / 高 0%
バランス状態:
✅ 高凝集 (強い結合 + 近い距離): 24 (6%) ← 理想的
✅ 疎結合 (弱い結合 + 遠い距離): 5 (1%) ← 理想的
🤔 許容可能 (強い結合 + 遠い距離 + 安定): 352 (92%)
優先的に対処すべき問題:
- 神モジュール (責務が多すぎる) | metrics
→ モジュールを分割: metrics_core, metrics_helpers
設計判断ガイド (Khononov):
✅ 強い結合 + 近い距離 → 高凝集 (理想的)
✅ 弱い結合 + 遠い距離 → 疎結合 (理想的)
🤔 強い結合 + 遠い距離 + 安定 → 許容可能
❌ 強い結合 + 遠い距離 + 頻繁に変更 → 要リファクタリング
Coupling Distribution
The tool shows how couplings are distributed by Integration Strength:
By Integration Strength:
| Strength | Count | % | Description |
|------------|-------|-----|--------------------------------|
| Contract | 23 | 4% | Depends on traits/interfaces |
| Model | 199 | 31% | Uses data types/structs |
| Functional | 382 | 59% | Calls specific functions |
| Intrusive | 46 | 7% | Accesses internal details |
Detected Issues
Critical Severity
- Circular Dependencies: Modules that depend on each other in a cycle
High Severity
- Global Complexity: Strong coupling spanning long distances
- Cascading Change Risk: Strong coupling with frequently changing components
- Hidden Coupling: Strong temporal co-change in Git history without a direct code dependency
Medium Severity
- God Module: Module with too many functions, types, or implementations
- High Efferent Coupling: Module depends on too many other modules
- High Afferent Coupling: Too many modules depend on this module
- Inappropriate Intimacy: Intrusive coupling across module boundaries
- Hidden Coupling: Moderate temporal co-change without a direct code dependency
- Accidental Volatility: Supporting or generic subdomain code churns like volatile core logic
Low Severity (hidden by default, use --all to show)
- Public Field Exposure: Public fields that could use getter methods
- Primitive Obsession: Functions with many primitive parameters (suggest newtype)
Hidden Coupling is based on the Khononov idea that functional coupling can be implicit: two modules may share a business rule or assumption even when the AST shows no import or call edge. Accidental Volatility applies the essential-vs-accidental distinction: frequent change is expected in a core subdomain, but suspicious in supporting or generic code.
Performance
cargo-coupling is optimized for large codebases with parallel AST analysis and streaming Git processing.
Benchmark Results (Large OSS Projects)
| Project | Files | With Git | Without Git | Speed |
|---|---|---|---|---|
| tokio | 488 | 655ms | 234ms | 745 files/sec |
| alacritty | 83 | 298ms | 161ms | 514 files/sec |
| ripgrep | 59 | 181ms | - | 326 files/sec |
| bat | 40 | 318ms | - | 126 files/sec |
Performance Features
- Parallel AST Analysis: Uses Rayon for multi-threaded file processing
- Optimized Git Analysis: Streaming processing with path filtering
- Configurable Thread Count: Use
-j Nto control parallelism
# Show timing information
cargo coupling --timing ./src
# Use 4 threads
cargo coupling -j 4 ./src
# Skip Git analysis for faster results
cargo coupling --no-git ./src
Git Analysis Optimization
The Git volatility analysis is optimized with:
- Path filtering:
-- "*.rs"filters at Git level (reduces data transfer) - Diff filtering:
--diff-filter=AMRCskips deleted files - Streaming:
BufReaderprocesses output without loading all into memory - Async spawn: Starts processing before Git completes
These optimizations provide 5x-47x speedup compared to naive implementation on large repositories.
Library Usage
use cargo_coupling::{
analyze_workspace_with_config, build_manifest, generate_report_with_thresholds,
load_compiled_config, IssueThresholds, ManifestContext, VolatilityAnalyzer,
};
use std::path::Path;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let path = Path::new("./src");
let config = load_compiled_config(path)?;
// Analyze project with workspace support and config-aware excludes
let mut metrics = analyze_workspace_with_config(path, &config)?;
// Add volatility from Git history
let mut volatility = VolatilityAnalyzer::new(6);
if let Ok(()) = volatility.analyze(path) {
metrics.file_changes = volatility.file_changes;
metrics.update_volatility_from_git();
}
// Detect circular dependencies
let circular = metrics.circular_dependency_summary();
if circular.total_cycles > 0 {
println!("Found {} cycles!", circular.total_cycles);
}
// Generate report with custom thresholds
let thresholds = IssueThresholds {
max_dependencies: 20,
max_dependents: 25,
..Default::default()
};
let manifest = build_manifest(&ManifestContext {
git_used: !metrics.file_changes.is_empty(),
tests_excluded: config.exclude_tests,
parse_failures: 0,
});
generate_report_with_thresholds(&metrics, &thresholds, &manifest, &mut std::io::stdout())?;
Ok(())
}
Docker
Run cargo-coupling without installing Rust:
# Basic analysis
docker run --rm -v $(pwd):/workspace ghcr.io/nwiizo/cargo-coupling coupling /workspace/src
# Summary mode
docker run --rm -v $(pwd):/workspace ghcr.io/nwiizo/cargo-coupling coupling --summary /workspace/src
# Web UI (access at http://localhost:3000)
docker run --rm -p 3000:3000 -v $(pwd):/workspace ghcr.io/nwiizo/cargo-coupling coupling --web --no-open /workspace/src
# Japanese output
docker run --rm -v $(pwd):/workspace ghcr.io/nwiizo/cargo-coupling coupling --summary --jp /workspace/src
Docker Compose
# Run analysis
docker compose run --rm analyze
# Start Web UI
docker compose up web
Available Tags
| Tag | Description |
|---|---|
latest | Latest release |
main | Latest main branch build |
vX.Y.Z | Specific version |
CI/CD Integration
# .github/workflows/coupling.yml
name: Coupling Analysis
on: [push, pull_request]
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for volatility analysis
- name: Install cargo-coupling
run: cargo install cargo-coupling
- name: Run coupling analysis
run: cargo coupling --summary --timing ./src
- name: Quality gate check
run: cargo coupling --check --min-grade=C --max-circular=0 ./src
- name: Ratchet against main
run: cargo coupling --check --baseline origin/main --fail-on=high ./src
- name: Generate report
run: cargo coupling -o coupling-report.md ./src
- name: Upload report
uses: actions/upload-artifact@v4
with:
name: coupling-report
path: coupling-report.md
Quality Gate Options
The --check command provides flexible quality gate configuration:
# Fail if grade is below C
cargo coupling --check --min-grade=C ./src
# Fail if there are any circular dependencies
cargo coupling --check --max-circular=0 ./src
# Fail if there are any critical issues
cargo coupling --check --max-critical=0 ./src
# Fail on any high severity or above
cargo coupling --check --fail-on=high ./src
# Fail only when this change introduces new high severity or above issues
cargo coupling --check --baseline origin/main --fail-on=high ./src
# Combine multiple conditions
cargo coupling --check --min-grade=B --max-circular=0 --max-critical=0 ./src
Exit codes:
0: All checks passed1: One or more checks failed
Best Practices
✅ Good: Strong Coupling at Close Distance
mod user_profile {
pub struct User { /* ... */ }
pub struct UserProfile { /* ... */ }
impl User {
pub fn get_profile(&self) -> &UserProfile { /* ... */ }
}
}
✅ Good: Weak Coupling at Far Distance
// core/src/lib.rs
pub trait NotificationService {
fn send(&self, message: &str) -> Result<()>;
}
// adapters/email/src/lib.rs
impl NotificationService for EmailService { /* ... */ }
❌ Bad: Strong Coupling at Far Distance
// src/api/handlers.rs
impl Handler {
fn handle(&self) {
// Direct dependency on internal implementation ❌
let result = database::internal::execute_raw_sql(...);
}
}
❌ Bad: Circular Dependencies
// module_a.rs
use crate::module_b::TypeB; // ❌ Creates cycle
// module_b.rs
use crate::module_a::TypeA; // ❌ Creates cycle
Limitations
This tool is a measurement aid, not an absolute authority on code quality.
Please keep the following limitations in mind:
What This Tool Cannot Do
- Understand Business Context: The tool analyzes structural patterns but cannot understand why certain couplings exist. Some "problematic" patterns may be intentional design decisions.
- Replace Human Judgment: Coupling metrics are heuristics. A high coupling score doesn't always mean bad code, and a low score doesn't guarantee good design.
- Detect All Issues: Static analysis has inherent limitations. Runtime behavior, dynamic dispatch, and macro-generated code may not be fully analyzed.
- Provide Perfect Thresholds: The default thresholds are calibrated for typical Rust projects but may not fit every codebase. Adjust them based on your project's needs.
Important Considerations
- External Dependencies Are Excluded: The health grade only considers internal couplings. Dependencies on external crates (serde, tokio, etc.) are not penalized since you cannot control their design.
- Git History Affects Volatility: If Git history is unavailable or limited, volatility analysis will be incomplete.
- Blind Spots Are Explicit: Text output shows run-specific notes and a pointer to the full "Not Analyzed" declaration. Use
--blind-spotsor--allfor the full text list;--jsonand--aialways include it. - Small Projects May Score Differently: Projects with very few internal couplings (< 10) may receive a Grade B by default, as there's insufficient data for accurate assessment.
Recommended Usage
- Use as a Starting Point: The tool highlights areas worth investigating, not definitive problems.
- Combine with Code Review: Human review should validate any suggested refactoring.
- Track Trends Over Time: Use
--historyregularly to track coupling trends rather than focusing on absolute scores. - Customize Thresholds: Adjust
--max-depsand--max-dependentsto match your project's architecture.
The goal is to provide visibility into coupling patterns, empowering developers to make informed decisions.
References
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.