nimbus graph AccountService prints what reaches a class. nimbus graph --cycles lists every circular dependency. nimbus graph AccountService --out graph.md writes a Mermaid document that GitHub, VS Code and Obsidian render natively. It is free, and it reads source rather than calling an org.
What the graph is for
- Onboarding. A new engineer's first question about an unfamiliar codebase is structural, and the answer is usually folklore. A graph makes it checkable.
- Refactoring. Before you change a service class you want the blast radius: who calls it, and how far the call chain goes.
- Pull-request review. A reviewer looking at a diff cannot see what else depends on the file being changed. A diagram in the PR description costs one command and removes a whole class of "did you know X uses this?" comments.
- Cycles. Apex tolerates circular class dependencies, so they accumulate silently and then make everything hard to test in isolation. Nobody finds them by reading.
What you can do without extra tooling
The baseline is grep, which gets you one hop. It is genuinely useful and costs nothing.
# Who mentions this class at all?
grep -rn "AccountService" force-app --include="*.cls" --include="*.trigger"
# Who mentions it and is not the class itself
grep -rln "AccountService" force-app --include="*.cls" | grep -v "AccountService.cls"What grep will not do is follow the chain, tell you which of those references are tests, or find a cycle. For that you need something that parses the code. Salesforce's own Setup → Dependencies view and the Tooling API MetadataComponentDependency object answer some of this, but they answer it about a deployed org rather than the branch on your machine, which is the wrong thing during a refactor.
The three questions with nimbus graph
nimbus graph reads your project source and answers the three questions. It needs no org, no deployment, and no network.
# What reaches this class, directly and transitively —
# plus which tests relate to it
nimbus graph AccountService
# Bound the neighbourhood (default is 2 hops)
nimbus graph AccountService --depth 1
# Every circular dependency, largest first
nimbus graph --cyclesDepth matters on a real project. The default of two hops keeps the answer readable; widening it on a codebase with a central utility class quickly produces something nobody will look at.
A diagram that renders in the pull request
Raw Mermaid is not meant to be read in a terminal. Write it to a .md file and Nimbus wraps the diagram in a Markdown document, with the cycles listed underneath and the limits written out as prose. GitHub, the VS Code preview and Obsidian all render it natively, with no image to regenerate and no Graphviz needed on the reviewer's machine.
# Markdown + fenced Mermaid — paste-able into a PR
nimbus graph AccountService --out graph.md
# Mermaid to stdout, if you are piping it somewhere
nimbus graph AccountService --format mermaid
# Graphviz, for a proper SVG
nimbus graph --format dot | dot -Tsvg -o graph.svg
# Machine-readable: schema nimbus.graph/v1
nimbus graph AccountService --jsonPast 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.
Exploring it interactively
A static image is fine for a PR and poor for exploration. The same nimbus.graph/v1 payload drives an interactive viewer in three hosts:
- Dev UI. Run
nimbus devand open the graph view. No install step, no marketplace cycle. - VS Code. 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, which is the thing a code graph can do that a note graph cannot.
- JetBrains. The IntelliJ 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.
What this graph does not tell you
This is a navigation aid rather than a test-selection decision, and the distinction matters.
nimbus graph reads syntactic references, meaning one class naming another in source. Salesforce codebases route a great deal of control through metadata instead: a trigger dispatching through a custom-metadata handler table reaches its handler with no reference anywhere for a parser to follow. On one real project, this syntactic reading found 1 of the 46 test classes that genuinely exercise a handler. If you used a static graph to decide which tests to skip, you would skip 45 tests that matter.
So where a coverage map exists, Nimbus prints a second, independent answer beside the first: which tests a real run observed executing lines in that file, whatever the mechanism. The two are deliberately never merged into one number, because a merged figure would hide the only thing worth looking at. Where the readings disagree, the disagreement is the finding. 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.
The full argument, including the measurement, is on the dependency graph page.
Next steps
Run nimbus graph --cycles on your project once. On most codebases of any age it finds something, and the largest cycle is usually the reason a particular area is hard to test.