8 — Apex Analysis and Packaging

Apex analysis and packaging

The Apex tab is two-paned. The left is a dependency tree rooted at each Apex trigger; the right is a detail view of whichever class you click.

How the dependency tree is built

For each enabled object Stood Flows finds the Apex triggers and walks their full dependency chain — every Apex class a trigger calls, every class those classes call, and so on. There is no depth limit: if your trigger pulls in 250 classes through six layers of helpers and utility libraries, all 250 end up in the tree. Classes reachable from several entry points are recorded once (a diamond shared by two triggers appears once, not twice).

The class bodies fetched along the way are saved locally under workspaces/<alias>/graphs/<slug>/apex/ (classes/<Name>.cls, triggers/<Name>.trigger) so subsequent analyses don't have to re-download them. Managed-package code with hidden bodies is recorded but the body is left empty — Salesforce doesn't expose it.

What's in the right pane

When you click a node, the right pane shows:

Chips on the tree

The little badges next to each class name in the tree:

These are computed once and persisted in apex-panel.json.

Refresh Mapping

The Refresh Mapping button at the top of the left pane re-runs the local analysis against the saved .cls / .trigger bodies. It's quick — no org calls — and you'd reach for it in two situations:

Refresh Mapping does not re-fetch from the org — that requires a full Refresh.

Generate schema artefacts (the migration package)

Click a class, then click Generate schema artefacts in the right pane header. This produces a self-contained package under:

workspaces/<alias>/graphs/<slug>/apex-packages/<className>/
├── manifest.json # summary: class count, sObject count, generation time
├── dependency-tree.json # the tree rooted at this class, structural
├── classes/ # mirrored .cls bodies for every class in the chain
│ ├── MyClass.cls
│ ├── DependencyA.cls
│ └── …
└── sfschemas/ # one schema per SObject the chain touches
├── Account.sfschema
├── Opportunity.sfschema
└── …

What's in an .sfschema file

Each sfschema is the full describe of an SObject — fields, types, picklist values, relationships — followed by an access patterns block:

object Lead {
Id : Id
Name : String(255)
}
// Access patterns for Lead observed in Apex package "MyClass".
// 4 SOQL query/queries:
soql SOQL_Id_Name_Status_W_Status: SELECT Id, Name, Status FROM Lead WHERE Status = 'Open'
soql SOQL_Id_Lim200: SELECT Id FROM Lead LIMIT 200
soql SOQL_Id_Email_W_IsConverted: SELECT Id, Email FROM Lead WHERE IsConverted = false
soql SOQL_count_GROUP_W_Source: SELECT COUNT(Id) FROM Lead GROUP BY LeadSource
// DML operations:
dml INSERT
dml UPDATE

soql and dml are first-class statements (not comments), so a downstream team can parse them programmatically — they sit on their own lines with a stable, easy-to-match shape. The migration target is the contract: any replacement service must support these access patterns or document how it's migrated past them.

Const names are derived from the query shape:

Identical queries from multiple classes share one const. Near-misses (different fields, different WHERE) get distinct names.

Why this is a migration package

The use case: an architect wants to replace an Apex class chain with an out-of-org service (a microservice, a worker, a separate SF org). The runtime boundary will be HTTP + a schema contract; the contract must reflect every SObject the Apex chain reads or writes today. The sfschema files give the consumer team:

You hand this package off and the receiving team has everything they need to scope and validate the migration.

What to do with the Code KPIs

The Code column group in the KPI table shows aggregates per trigger:

A trigger with Size > 200 000 characters and Deps > 100 is almost always worth extracting. Use the Apex tab to walk the tree, identify the actual business logic vs the framework/utility classes, and generate a package for the business logic only.

Common gotchas

Published with Nuclino