Map Salesforce dependencies before you change fields, Flows or Apex
Use sf-intelligence to trace what depends on a Salesforce field, object, Flow, Apex class, layout, permission set, integration endpoint, or package boundary before you change it.
Last updated 2026-07-29
trace dependencies both ways in 2 minutes - no org, no auth
claude mcp add --transport stdio --scope user sf-intelligence-demo -- npx -y sf-intelligence demoThen run the three worked examples below against real (synthetic) metadata: "what depends on Invoice__c.Amount__c?" for a field, "what does the Installation_On_Complete flow depend on, and what depends on it?" for a Flow traced both directions, and "what does PaymentService depend on, and what calls it?" for an Apex call graph with its blind spots named. Synthetic "Verdant Energy" org, served fully offline: no Salesforce login, no sf CLI. more on the demo →
Why Salesforce dependencies are hard to see
Salesforce dependencies live across XML metadata, Apex source, formulas, Flow definitions, Profile and Permission Set grants, page layouts, packages, and integration configuration. A single field can be referenced by automation, code, reports, validation rules, and external systems. sf-intelligence collects those signals into a local graph and reports how each dependency was found.
Dependency analysis targets
| Change target | What to inspect |
|---|---|
| Field | Formulas, Flows, validation rules, Apex references, layouts, FLS, reports |
| Object | Child relationships, automations, Apex queries, permissions, tabs, record types |
| Flow | Entry criteria, called subflows, field writes, Apex actions, scheduled paths |
| Apex class | Call graph, trigger entry points, tests, integrations, async jobs |
| Permission set | Object and field grants, app access, tab access, assigned users through live reads |
| Package | Namespaced components, local references, uninstall and upgrade blast radius |
Searches this page answers
- "Salesforce dependency analysis for custom fields"
- "Where is this Salesforce field used?"
- "What depends on this Flow?"
- "What breaks if I remove a picklist value?"
- "What Apex classes call this method?"
- "What is the impact of changing a permission set?"
Evidence-first output
The output is designed for review before a change lands. Each dependency points to a canonical component ID, the edge type, the confidence tier, and any known coverage caveat. If a metadata family was not retrieved, sf-intelligence says that the scan is partial instead of implying safety.
Three worked examples
Each example shows the question you type and the shape of the answer you get back. The output is a static picture of what the retrieved metadata references - not a runtime trace and not proof of what executes.
1. Before deleting a field
You are about to delete Account.Industry__c and want the upstream and downstream references first. This is the same trace behind where is a Salesforce field used, read here as a pre-change dependency check.
› what depends on Account.Industry__c before I delete it? ▸ 5 components reference CustomField:Account.Industry__c ValidationRule:Account.Industry_Required references declared Flow:Lead_To_Account_Sync writesField parsed ApexClass:AccountService (2 refs) readsField parsed Layout:Account-Sales Layout references declared Report:Accounts_by_Industry references declared provenance offline_snapshot coverageCaveat: layouts & reports fully modeled; dynamic Apex not parsed
Read this as a static shape: five families reference the field today. If a family comes back empty, that is no static evidence found for that family - not a verdict that the field is safe or unused. Weigh these dependents with the full delete simulation on Salesforce impact analysis.
2. Tracing a Flow both ways
You want to know what a record-triggered Flow reads and writes, and whether anything else invokes it before you deactivate it.
› what does the Opportunity_Router flow depend on, and what depends on it? ▸ Flow:Opportunity_Router — record-triggered, after save on Opportunity depends on ↓ CustomField:Opportunity.StageName readsField parsed CustomField:Account.Health__c writesField parsed ApexClass:NotifyCsm invokes declared depended on by ↑ no static evidence found — no retrieved component references this Flow provenance offline_snapshot · confidence: parsed
The empty upstream line is deliberate wording: the graph found no static evidence that another component calls this Flow, which is different from proving nothing does. A subflow invoked by a dynamically built name, or an invocation added since the last refresh, would not appear.
3. An Apex class call graph
You are refactoring an Apex class and need its resolved field access, its call graph, and - just as important - what the static parse could not see.
› what does BillingService depend on, and what calls it? ▸ ApexClass:BillingService depends on ↓ CustomField:Invoice__c.Amount__c readsField parsed CustomField:Invoice__c.Status__c writesField parsed ApexClass:TaxCalculator calls parsed depended on by ↑ ApexTrigger:InvoiceTrigger calls parsed ApexClass:BillingServiceTest calls parsed blind spots on this class 1 dynamic SOQL string not resolved — Database.query(...) reflective field access via getPopulatedFieldsAsMap() not tracked provenance offline_snapshot · confidence: parsed + heuristic
The parsed edges come from the default ANTLR pass over the Apex source; the blind-spot lines are shapes the parser explicitly flags rather than silently dropping. Treat the resolved edges as high-trust and the blind spots as prompts to read the source before you act.
How edge confidence works
Every dependency edge carries one of four tiers. The tier tells you how the edge was found, which is what governs how much to trust it. This is the per-edge confidence - distinct from a reasoning claim's confidence.
| Tier | How the edge was found | How much to trust it |
|---|---|---|
declared | Salesforce metadata states the dependency directly - FLS, layout, validation rule, record type, lookup / master-detail | Highest - it is written in the XML |
parsed | ANTLR/AST parse of Apex or XML parse of a Flow or formula - resolved field reads and writes, cross-class calls, field-level SOQL | High - grounded in the real source |
heuristic | Regex/token backfill where the parser failed or the reference is only textual | Lower - can false-positive; spot-check before acting |
partial | A metadata family was not retrieved this refresh | Not evidence of absence - "not checked", never "none" |
Static analysis has hard blind spots, and sf-intelligence names them rather than papering over them:
- Dynamic SOQL / SOSL strings - queries assembled at runtime (
Database.query(someString)) reference fields the parser cannot resolve. - Reflective field access -
getPopulatedFieldsAsMap(),getGlobalDescribe(), and dynamicget()/put()touch fields by name at runtime, invisibly to a static scan. - Cross-method dataflow - a value read in one method and written in another is not traced end to end, so some transitive field dependencies are not linked.
Because of these gaps, an empty result is reported as no static evidence found, never as "unused." A dependency finding describes a static shape in the retrieved metadata; it is not proof of what runs in production.
Coverage: what dependency analysis sees and misses
| Metadata family | Edge confidence | Captured | Not captured |
|---|---|---|---|
| Layouts, field-level security, validation rules, record types, lookups | declared | Direct references from the retrieved XML | — |
| Flows & formulas | parsed | Field reads/writes, subflow calls, formula field references | Which branch a given record takes (needs record data) |
| Apex - default ANTLR pass | parsed | Resolved field reads/writes, cross-class calls, field-level SOQL | Dynamic SOQL/SOSL strings, reflective field access, cross-method dataflow |
| Apex - regex backfill on parse failure | heuristic | Textual field / class mentions the parser missed | False positives; confirm before acting |
| Any family the refresh skipped | partial | — | Reported as "not checked", never "none in org" |
Run sfi.coverage_report to see which families your last refresh actually retrieved. A type listed under notModeled means it was not checked - so any absence-based answer over that type is only as strong as that coverage.
Related pages
For change-specific blast radius examples, read Salesforce impact analysis. To list every place a single field is referenced, read where is a Salesforce field used. For broader org inventory, read Salesforce metadata analysis. For installation in an AI client, read Claude Salesforce MCP setup.
Trace dependencies before you change metadata.
Run a read-only refresh, then ask dependency questions in plain language.