nimbus graph reads your Salesforce Apex source and answers three questions: what reaches a class, where the circular dependencies are, and what shape the codebase is. Free, in the CLI and in an interactive viewer. It is also candid about the one thing a static graph cannot see — which is most of why it is worth trusting.
nimbus.graph/v1 payload nimbus graph --json emits, 13 of its 42 edges seen only by a coverage run. Hover a node for its neighbourhood, click to pin. The live viewer — forces, search, drag — ships in nimbus dev and both editor plugins.Bare nimbus graph prints the orientation view: class and trigger counts, edge count, cycles, self-references, and the most-depended-upon classes. It is the step before you know which class to ask about, and it runs against your local source — no org, no network.
Every view ends with what it does not establish. Those limits are printed, not buried: they travel in the JSON payload, in the DOT and Mermaid exports as comments, and in a Markdown export as prose — because a comment is invisible once the diagram is rendered, which is exactly when someone is most likely to read the picture as complete.
$ nimbus graph
Dependency graph — 81 classes, 1 triggers, 12 edges
Circular dependencies 0
Self-references 2
Observed-only edges 0
Most depended upon
FflibDescribeMock 2 dependents
AccountService 1 dependents
CollectionUtils 1 dependents
Next
nimbus graph <Class> what reaches one class
nimbus graph --cycles circular dependencies in full
nimbus graph --format dot export for GraphvizPass a class name and Nimbus reports what references it, directly and transitively, plus which test classes reach it. --depth bounds the neighbourhood (default 2) so the answer stays readable on a large project.
--cycles lists every circular dependency, largest first, with a shortest loop through each. --format dot|mermaid|json exports the graph; --json emits the full nimbus.graph/v1 payload that the CLI, the Dev UI and both editor extensions all read.
Past 300 nodes an export refuses and tells you how to narrow, rather than emitting an unreadable hairball. Whole-project renders stop being useful well before they stop being possible.
# What reaches one class
nimbus graph AccountService
# Circular dependencies, largest first
nimbus graph --cycles
# Bound the neighbourhood
nimbus graph AccountService --depth 2
# Export for Graphviz
nimbus graph --format dot | dot -Tsvg -o graph.svg
# Raw Mermaid is not meant to be read in a terminal.
# Write a .md and open it — VS Code, GitHub and
# Obsidian all render the diagram.
nimbus graph AccountService --out graph.md
# Machine-readable: schema nimbus.graph/v1
nimbus graph AccountService --jsonA class query returns the syntactic reading — who references this class in source. Where a coverage map exists, Nimbus prints a second, independent answer beside it: which tests a real run observed executing lines in that file, whatever the mechanism.
The two are never combined into one number. A merged figure would hide the only thing worth looking at. Where the readings disagree, the disagreement is the finding — and on a metadata-dispatch codebase it is a large one.
Observed-only edges are drawn dashed in every export and called out in the viewer legend, so a class reachable only through dispatch looks different at a glance.
$ nimbus graph ACCT_IndividualAccounts_TDTM
ACCT_IndividualAccounts_TDTM — reachability
Tests reached syntactically (1)
ACCT_IndividualAccounts_TEST
Tests a coverage run actually observed
executing it (46)
ACCT_AdministrativeNameRefresh_TEST
...
Observed but NOT reachable syntactically (45)
— the blind spot, measured
Not established by this view
- Syntactic references only. A trigger
dispatching through custom metadata
reaches handlers with no reference
to follow.
- This is reachability, not a
test-selection decision.A picture implies completeness in a way a list does not, so this is stated plainly rather than footnoted. The graph reads syntactic references. We measured that reading against real coverage across five Salesforce projects, inverting the graph to ask which tests reach a changed class, and comparing it against which tests a full coverage run actually observed executing that class.
On one real project the syntactic reading found 1 of the 46 test classes that genuinely exercise a trigger handler. The handler is reached by metadata-driven dispatch: the trigger fires, the framework looks the handler up in custom metadata, and calls it. No test references the handler. No class references the handler. There is no edge for a syntactic analysis to follow, and no amount of parser work creates one.
That is why nimbus graph refuses to present itself as a way to decide which tests to skip. A selection that silently drops 45 of the 46 tests exercising a changed handler returns green for code nothing executed, and a green result you cannot trust is worse than a slower one you can. Related: a changed .trigger file resolves to no class in the graph at all — nimbus test --impacted therefore forces a full run when a trigger changes, for the same reason.
What survives the measurement is the read-only use: blast radius, orientation, cycles, and a diagram to argue over in a pull request. Those are what this page sells, and they are what the command does.
The same graph is explorable rather than printed. It reads like a note-graph tool: hover a class and its direct neighbourhood lights up while the rest fades; click to pin the highlight, and the pinned class’s edges grow arrowheads so dependency direction is there exactly when you are looking for it. Search filters by name. Test classes and unconnected nodes can be hidden, the layout forces are adjustable, and nodes can be dragged.
Node size follows dependent count and colour follows kind — class, trigger, test. Labels follow a zoom budget: hubs first, the rest as you come closer, because drawing every label at once on a real org produces a hairball and nothing else. The layout engine stays fluid at a thousand classes and stops hub classes collapsing the graph into a knot. The legend names the dispatch blind spot; it is not hidden behind a tooltip.
Run nimbus dev and open the graph view. The renderer landed here first: no install step, no marketplace cycle, and the same nimbus.graph/v1 payload the CLI emits.
The Nimbus: Show Dependency Graph command opens the graph in a webview. Single click pins a class and its neighbourhood; double click opens the file — the thing a code graph can do that a note graph cannot.
The JetBrains plugin embeds the identical renderer. One renderer, three hosts, one payload — the graph you read in the terminal and the graph you read in the IDE are the same graph.
nimbus graph <Class> --out graph.md writes a Markdown document with a fenced Mermaid block, the cycles listed underneath, and the limits written as prose. GitHub, VS Code preview and Obsidian all render it natively — no image to regenerate, no Graphviz on the reviewer's machine.
Bare nimbus graph prints the shape of the project: how many classes and triggers, how many edges, the cycle count, and the most-depended-upon classes. That is the step before you know which class to ask about. Then narrow with a class name and a depth.
nimbus graph --cycles reports every strongly connected component largest first, each with a shortest loop through it, so the output says where to cut rather than only that a cycle exists. Self-references are counted separately — in Apex they are usually a class qualifying its own statics.
nimbus graph is part of the free tier — no org, no license key, no sign-up. Install Nimbus, run it in your project root, and the first answer arrives before you have finished reading the command.