Dependency Graph

What does changing
this class reach?

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 also states plainly the one thing a static graph cannot see.

AccountTriggerBrewAccountHandlerBrewAccountHandlerTestBrewAuditArchiveBrewAuditLoggerBrewBulkScenarioTestBrewCustomerMetricsBrewInventoryServiceBrewInventoryServiceTestBrewLoyaltyEngineBrewLoyaltyEngineTestBrewOrderRouterBrewOrderRouterTestBrewPricingServiceBrewRegionalConfigBrewSharedSetupTestBrewShippingCalloutBrewSubscriptionApiBrewSubscriptionApiTestBrewSubscriptionHandlerBrewSubscriptionHandlerTestBrewSubscriptionServiceBrewSubscriptionServiceTestBrewTestFactoryBrewWelcomeMailerBrewWelcomeMailerTestHoplogIBrewShippingProviderSubscriptionTrigger
  • class — sized by transitive dependents in this view
  • trigger — no static inbound edge; dashed edges are observed
  • test class
  • observed-only — no syntactic reference
Real dependency data from a demo Salesforce project: a 17-class, 10-test, 2-trigger slice of the nimbus.graph/v1 payload nimbus graph --json emits, 26 of its 55 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.

Start with the shape of the project

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 with no org and no network.

Every view ends with what it does not establish. Those limits are printed with the output: 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 when someone is most likely to read the picture as complete.

bash
$ 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 Graphviz

From the command line

Pass 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.

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

Two answers that are never merged

A class query returns the syntactic reading, meaning 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.

bash
$ 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.

The graph is a navigation aid and cannot select tests.

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 has no class counterpart in the static dependency graph used for selection, so nimbus test --impacted 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 rule this follows: no label in the CLI, the exports, or any of the three viewers reads “impact” or “affected tests”. Reported cycles are a lower bound for the same reason, because edges invisible to a syntactic reading may close further loops.

The interactive viewer

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 transitive dependent count in the current view, 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. 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.

Dev UI

nimbus dev

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.

VS Code

Show Dependency Graph

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.

IntelliJ

Same renderer, JCEF webview

The JetBrains plugin embeds the identical renderer. There is one renderer, three hosts and one payload, so the graph you read in the terminal and the graph you read in the IDE are the same graph.

Uses

Pull requests

A diagram reviewers actually see

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

Onboarding

Orientation on a codebase you did not write

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.

Refactoring

Circular dependencies

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.

A map of the codebase before you change it.

nimbus graph is part of the free tier, with no org, no license key, and no sign-up. Install Nimbus and run it in your project root.