dotnet-coupling
0.6.1
dotnet tool install --global dotnet-coupling --version 0.6.1
dotnet new tool-manifest
dotnet tool install --local dotnet-coupling --version 0.6.1
#tool dotnet:?package=dotnet-coupling&version=0.6.1
nuke :add-package dotnet-coupling --version 0.6.1
dotnet-coupling
Experimental coupling balance analyzer for C#/.NET projects.
dotnet-coupling scans C# source and reports coupling risks using three
dimensions from Vlad Khononov's balancing-coupling model:
- integration strength
- distance
- volatility from Git history
By default, dotnet-coupling runs in syntax mode for broad compatibility.
It also provides an opt-in semantic preview for .csproj, .sln, and
.slnx inputs so project boundaries and symbol-aware dependencies can be
inspected without changing the default contract.
Experimental project
Grades, thresholds, and detected patterns are still being tuned through dogfooding and OSS feedback. Treat the report as decision support, not as a proof that a codebase is free of coupling risk.
Table of Contents
- Quick Start
- What It Reports
- Common Commands
- Syntax and Semantic Modes
- Configuration
- Baseline Gate
- SARIF and Hotspots
- Investigation
- Output and Schema
- CI and Quality Gates
- Current Blind Spots
- Development
- License
Quick Start
1. Install
dotnet tool install --global dotnet-coupling
2. Analyze
dotnet-coupling ./src
dotnet-coupling --summary ./src
dotnet-coupling --json ./src
3. Gate in CI
dotnet-coupling --check --min-grade B ./src
dotnet-coupling --check --baseline main --fail-on High ./src
dotnet-coupling --sarif --output dotnet-coupling.sarif ./src
Example summary:
Grade: A | Avg Score: 0.79 | Basis: issue-density
Files: 24 | Types: 63 | Couplings: 201 internal / 0 external
Issues: 0 Critical, 0 High, 12 Medium
Git: disabled (--no-git)
What It Reports
The default report highlights the overall grade, average balance score, issue counts, and the most important coupling problems found in the analyzed scope.
The analyzer currently focuses on:
- structural dependencies observed from source
- project / assembly / NuGet package boundaries in semantic mode
- Git co-change data for volatility and hidden coupling heuristics
- issue patterns such as global complexity, circular dependency, hidden coupling, scattered external coupling, and fan-in / fan-out concentration
Common Commands
# Default text report
dotnet-coupling ./src
# Summary only
dotnet-coupling --summary ./src
# Machine-readable JSON
dotnet-coupling --json ./src
# GitHub Code Scanning compatible SARIF
dotnet-coupling --sarif --output dotnet-coupling.sarif ./src
# Top refactoring candidates
dotnet-coupling --hotspots 10 ./src
# Reverse dependency impact
dotnet-coupling --impact MyApp.Domain.Order --depth 3 ./src
# Semantic member trace
dotnet-coupling --trace Save --mode semantic ./sample.sln
# Shareable and coding-agent reports
dotnet-coupling --markdown --impact Order ./src
dotnet-coupling --ai ./src
# Skip Git history for faster local runs
dotnet-coupling --no-git ./src
# Apply JSON or TOML config
dotnet-coupling --config .coupling.json ./src
dotnet-coupling --config .coupling.toml ./src
# CI gate on minimum grade
dotnet-coupling --check --min-grade B ./src
# Ratchet gate against a baseline branch
dotnet-coupling --check --baseline main --fail-on High ./src
# Opt in to semantic preview for project-aware analysis
dotnet-coupling --mode semantic --summary ./sample.sln
Syntax and Semantic Modes
syntax mode is the default and is the stable compatibility path today.
- works well against plain source directories
- avoids workspace-loading prerequisites
- preserves the established CLI and JSON contract
semantic mode is preview-only and must be enabled explicitly.
- accepts
.csproj,.sln, and.slnx - resolves project graph, assembly boundaries, package references, and more symbol-aware dependencies
- emits recoverable workspace diagnostics when the environment cannot fully load the target
Configuration
JSON and TOML configuration are supported via --config <file>. Automatic
discovery checks .coupling.json and coupling.json first, then
.coupling.toml and coupling.toml.
Current supported settings include:
- analysis excludes
- test project path patterns for issue-noise reduction
- fan-in and fan-out thresholds
- temporal coupling thresholds
- scattered external breadth thresholds
- ignore rules for paths, namespaces, issue types, and precise issue suppressions
- domain context for user-supplied core/supporting/generic subdomain categories and expected volatility
- role context for strategic boundaries and technical areas
- complexity thresholds and weight for remediation prioritization
TOML keys use snake_case; JSON keeps the existing camelCase schema shape.
analysis.test_projects / analysis.testProjects marks test project files.
Those couplings remain visible as observed data, but couplings whose source file
matches a test project pattern are excluded from active issue detection and
grade calculation.
Domain context does not infer subdomain categories; it uses only the paths and
categories provided in config. Supporting or generic subdomains with high
observed Git churn and lower expected volatility are reported as
AccidentalVolatility. Subdomain names must be unique. If multiple path
patterns match the same component, the first matching subdomain in the config
is used. Domain paths are repository/workspace-relative, so moving the config
file or passing it with --config does not change their meaning. Summary output
and JSON manifest.domainContext show how many components matched configured
subdomains and how many AccidentalVolatility issues were produced.
Role context separates strategic boundary meaning from technical scoring hints.
strategicRole / strategic_role can describe boundary meanings such as
sharedKernel / shared_kernel and remains advisory. domain.areas assigns
technical roles such as domainModel, applicationService, adapter,
compositionRoot, contract, and testSupport. contract targets and
compositionRoot sources reduce over-reporting for intentional contracts and
orchestration, so technical roles can affect scores, issue counts, grades, and
--check exit codes. Other technical roles are shown in summaries, JSON
manifest data, and hotspot reasons without changing scoring.
Use contract for stable API shapes even inside a core subdomain: value
objects, identifier types, DTOs, published language models, and shared-kernel
contracts. Put these area patterns before broader domainModel patterns because
the first matching area wins. Use compositionRoot for Program, Startup,
module startup, DI registration, and host bootstrapping code. Summary output and
JSON run notes include coverage hints when configured subdomains or technical
roles leave components unmatched.
See .coupling.example.json,
.coupling.example.toml,
and schemas/dotnet-coupling-config-0.2.schema.json.
Baseline Gate
--baseline <ref> compares current issues with a Git ref and classifies them
as new, resolved, or unchanged.
With --check --baseline, the gate fails only for new issues at --fail-on
severity or higher. If --fail-on is omitted, the baseline gate uses High.
This makes it practical to adopt in an existing codebase without forcing a one-shot cleanup of all historical debt.
For long-lived accepted debt, use .coupling.json ignore.issues with a
stable (type, source, target) key and a required reason. Suppressed issues
are excluded from active counts, grade, --check, and SARIF upload, but remain
visible in summary and JSON output.
SARIF and Hotspots
--sarif emits SARIF 2.1.0 using Microsoft's Sarif.Sdk, with issue types
mapped to SARIF rules and source locations normalized to repository-relative
URIs for GitHub Code Scanning.
dotnet-coupling --sarif --output dotnet-coupling.sarif --no-git ./src
--hotspots [N] ranks the top coupling repair candidates from active issues,
fan-in, fan-out, volatility, boundary crossing, cycle participation, and
syntax-based cyclomatic/cognitive complexity. The default count is 10. Treat
hotspots as a remediation priority list; the project Grade remains the health
gate based on issue density. Complexity can change hotspot priority and reasons,
but it does not change issue severity, Grade, or --check exit codes.
Complexity uses the sonar-dotnet 10.27 C# metric profile. Cyclomatic
Complexity counts executable entry points and C# control-flow decisions.
Cognitive Complexity counts flow breaks, logical-operator sequences, and
nesting while discounting shorthand such as null coalescing. Top-level
statements are not attributed because the report model ranks named components.
dotnet-coupling --hotspots ./src
dotnet-coupling --json --hotspots 5 ./src
Investigation
--impact <component> performs a bounded, cycle-safe reverse dependency walk.
--trace <symbol> uses semantic observations to identify type or member callers;
it therefore requires --mode semantic and a project or solution input.
--depth defaults to 3, and 0 means unlimited traversal.
dotnet-coupling --impact Order --depth 2 ./src
dotnet-coupling --trace Repository.Save --mode semantic ./MyApp.sln
dotnet-coupling --json --impact Order ./src
dotnet-coupling --markdown --impact Order ./src
dotnet-coupling --ai --trace Repository.Save --mode semantic ./MyApp.sln
dotnet-coupling --summary --jp ./src
Markdown is intended for artifacts and PR summaries. --ai emits deterministic
evidence and bounded recommendations from the report; it does not call an LLM
or invent source changes. --jp / --japanese localizes human-readable output
without changing JSON or SARIF.
Output and Schema
JSON output includes:
$schemaandschemaVersion- analysis metadata
- issue-density grade
- issue counts and issue details
- manifest run notes and blind spots
- optional project-model metadata for project / assembly / package boundaries
- optional hotspots, hotspot complexity, and suppressed issues when requested
- optional impact or trace evidence in schema
0.5
Schema files live under schemas/.
CI and Quality Gates
Mutation score is the primary test-quality signal. Coverage is collected as a supporting signal to find unexercised areas, not as the main quality gate.
Current CI posture:
pull_request: build, test, format, package smoke, report aggregation, and coupling feedbackmainpush: build, test, format, package smoke, and report aggregation- same-repo PR / main push: coupling SARIF upload to GitHub Code Scanning
nightly-mutation: full Stryker run on schedule or manual dispatchrelease: build, test, format, pack, local tool smoke, publish
CI uploads coverage-report, semantic-self-report, dotnet-coupling-sarif,
dotnet-coupling-hotspots, and dogfood artifacts for inspection. octocov adds
line and branch coverage, the main-branch delta, and same-repository PR feedback.
Line coverage must remain at least 90%, branch coverage at least 80%, and neither
may regress by more than 0.1 percentage points. Mutation appears as not available
in PR CI and is reported by the nightly workflow instead.
Current Blind Spots
syntaxmode does not use semantic symbol resolution- semantic preview still depends on workspace prerequisites being loadable
- runtime DI container dependencies are not analyzed
- reflection and
dynamiccalls may be incomplete - generated code is excluded by default
Treat a clean report as "no observed issues", not as a guarantee that no coupling risk exists.
Development
dotnet restore dotnet-coupling.slnx --locked-mode
dotnet build dotnet-coupling.slnx --configuration Release --no-restore
dotnet test dotnet-coupling.slnx --configuration Release --no-restore
dotnet format dotnet-coupling.slnx --verify-no-changes --no-restore
dotnet test dotnet-coupling.slnx --configuration Release --results-directory TestResults/Coverage --coverage --coverage-output coverage.cobertura.xml --coverage-output-format cobertura
dotnet tool restore
dotnet tool run dotnet-stryker -- --config-file stryker-config.json
dotnet tool run dotnet-stryker -- --config-file stryker-complexity-config.json
Release history is collected in CHANGELOG.md.
License
MIT. See LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.