Bearing · free and open source · Apache 2.0
Do you understand your system?
Not is it healthy — that’s a different question, well served by other tools, for specialists. Bearing answers the one people ask right before they change something.
$ dotnet tool install -g IronMarten.Bearing
$ bearing ./MySolution.sln Zero configuration. No account. No network call.
A map, and a short list
Bearing reads a .NET solution and gives you two things: a map of the system, and a short list of the components that are unusual for what they are.
Who it’s for
A developer who needs to reason about a .NET codebase they don’t hold entirely in their head — because it’s large, because they’re new to it, or because they’re about to change something in it.
Why it’s called Bearing
Two meanings: get your bearings in a system too big to hold in your head, and the load-bearing components you should be careful with.
What you get
A terminal report that opens with one claim per kind of risk the run found, then describes the structure. Everything else is a flag, and nothing is written unless you ask for it.
bearing-terminal The terminal report's opening — one claim per kind of risk. bearing-html The --html report, top of page. --html — one shareable page, self-contained, no network.bearing-diagram The --diagram project map (SVG). --diagram — the project map, sized for pasting into chat.bearing-mosaic The --mosaic: every type as one cell (SVG). --mosaic — every type as one cell.bearing-plot The --plot: projects by reach and density (SVG). --plot — projects by reach and density.
And the whole model as data: --json — every finding, versioned — and
--csv — types, members and edges. bearing --help lists every flag
and every threshold the report cites, each with its default.
What it names
On nopCommerce, Bearing names roughly one type in eight — 592 findings across 12 kinds — and has nothing to say about the other seven, which is itself a statement about where not to look.
-
Circular references
Namespace cycles, project cycles and type tangles — the components that hold each other, so neither can be layered, understood or extracted without the other. Named, with the references that close the loop.
-
No static references found
Methods and types nothing in the solution refers to. It’s the dead-code question, labelled “verify before deleting”, because a static analysis can’t see every caller and says so.
-
Load-bearing and intricate
Many things depend on it, and it’s complex enough to hide a bug. Hard to change safely.
-
Bug blast radius
Widely depended on and internally complex: a bug here propagates.
-
Concealed decision
Complexity far above its peers while its connections are ordinary — something that looks like plumbing and isn’t.
-
Hub or god object
It depends on, and is depended on by, much of the system.
-
Spans architectural layers
Dependencies reaching across three or more architectural kinds — cross-cutting work, whatever the component is named.
-
Shared mutable state
Writes to static mutable state that every caller, on every thread, shares. Whether it’s contended is a runtime question; the sharing is certain from the code.
How it reports
Five rules every report follows.
-
Findings are sentences, not scores.
A finding is relative to the peer group a component should resemble. “Top 2% of your 56 normalizers” is a claim you can check; “Risk score 103,680” is one you can only argue with.
It never says “instability 0.296”. It says “19 things depend on this; it depends on 8 concrete types.”
-
There is no composite score.
Not on a dashboard, not in a tooltip, not as a CSV column. A deliberate, permanent constraint.
-
Silence is never a clean bill of health.
Every report says what it stayed quiet about — components with no peers, excluded generated code, projects that failed to load.
-
Name the specifics.
“Spans 3 architectural kinds” is arguable; “why is authentication calling
TenantStore?” is not. -
A finding you’ve decided about stays decided.
Acknowledge it in a committed file, with a reason, and the next run tells you what is new.
Stable from 1.0
The output is a contract. A breaking change to the command line, the acknowledgment file, the JSON or the CSV is a new major version.
Validated against nopCommerce, Jellyfin and Umbraco. Every threshold it cites was set by measurement on those, not chosen.
Requirements
The .NET 10 SDK. A solution that targets net8.0 or net9.0 is
analysed as it is — it doesn’t need to move. Restore the solution first.
What it will never do
- Observe runtime or traffic.
- Give a composite score or a grade.
- Generate explanations with AI. Findings are deterministic and auditable.
When you want it continuously
Bearing tells you what your system looks like today. Bearing Pro keeps that true for a team: history, pull-request comments that name only what a change introduced, and decisions that stay decided — self-hosted.
See Bearing Pro