How-to

Visualize Apex class dependencies

Three questions come up constantly on a large Salesforce codebase: what does this class touch, what touches it, and where are the cycles. Reading them out of the source by hand is slow and unreliable. Here is how to get all three from the command line, and — just as important — what a dependency graph of Apex cannot tell you.

Short version

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.

Why you want this

  • 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 honest baseline: grep gets you one hop. It is genuinely useful and costs nothing.

bash
# 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, not about the branch on your machine — which is the wrong thing during a refactor.

The Nimbus way

nimbus graph reads your project source and answers the three questions. No org, no deployment, no network.

bash
# 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 --cycles

Depth 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 a reviewer will actually see

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 — no image to regenerate, and no Graphviz needed on the reviewer's machine.

bash
# 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 --json

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.

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 dev and 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 — 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, not a test-selection decision, and the distinction is not pedantry.

nimbus graph reads syntactic references — 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 — 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.