| title | Dead-code analysis |
|---|---|
| description | Find unreferenced Apex classes safely before removing them. |
sf apx dead analyzes active, unmanaged Apex classes in an org and reports classes with
no known inbound references. It is an inventory and review aid; it does not delete or
modify org metadata.
The command requires both --target-org and --classes:
sf apx dead --target-org myOrg --classesUse --ignore one or more times to exclude case-insensitive class-name patterns. Patterns
support * and match the complete class name. The command also accepts --api-version and
--output-path (default: generated-files).
- It inventories active Apex classes where
NamespacePrefix = nulland fetches symbol tables in batches. - It reads
MetadataComponentDependencyrecords to build inbound and outbound references. - It reads the known AT4DX binding metadata objects and treats classes named by bindings as
live. The sources are:
ApplicationFactory_ServiceBinding__mdt,ApplicationFactory_SelectorBinding__mdt,ApplicationFactory_DomainBinding__mdt, andDomainProcessBinding__mdt. - It suppresses recognized entry points and test classes from classification. Recognized
entry points include REST resources, global classes, annotated or webservice methods,
and supported
Database.Batchable,System.Schedulable,System.Queueable,Messaging.InboundEmailHandler,System.Callable,Process.Plugin,Auth.RegistrationHandler,Database.RaisesPlatformEvents,System.Comparator,Iterable, andIteratorinterfaces. - It repeatedly removes zero-inbound classes. It also identifies orphaned dependency cycles and groups classes referenced only by their own tests.
Analysis continues in rounds so a class that becomes unreferenced only after an earlier round is reported as a cascade. Results include the round, reason, referrers, and DI risk where a class has an interface or base class but no matching binding.
DEAD: no inbound references, including classes found through cascade analysis.TEST-ONLY: referenced only by tests; its tests appear inTEST-OF-DEADwhen they can be removed with it.TEST-OF-DEAD: a test paired with a dead or test-only class.RETAINED: referenced by tests that also cover surviving code, so the class and tests must remain together.SUPPRESSED: a test, recognized entry point, or DI-bound class excluded from the active graph.
Suppression is always applied during classification. --include-suppressed only adds the
suppressed rows to human-readable output; it does not make those classes candidates for
dead/test-only buckets or destructive manifests. Suppressed rows include a WOULD BE DEAD
audit value and are sorted with yes rows first.
Add --destructive-manifest to write a reviewable manifest under
<output-path>/dead-code/:
sf apx dead --target-org myOrg --classes --destructive-manifestThe command writes destructiveChanges.xml for Apex classes and an empty package.xml.
By default, the manifest includes DEAD, TEST-ONLY, and paired TEST-OF-DEAD classes.
--dead-only limits it to the DEAD bucket and requires
--destructive-manifest.
Use --dry-run with --destructive-manifest to calculate and validate the manifest paths
without creating files. --classes without --destructive-manifest only reports findings.
The manifest never includes suppressed classes, regardless of --include-suppressed.
Review the generated XML before deployment:
sf project deploy start --target-org myOrg \
--manifest generated-files/dead-code/package.xml \
--post-destructive-changes generated-files/dead-code/destructiveChanges.xmlDependency data cannot see class names held in strings, such as Type.forName, anonymous
Apex scheduling, or bespoke configuration tables. Missing binding objects are reported and
mean DI implementations may be classified as dead. Classes without symbol tables use
naming conventions for test detection and cannot be checked for entry-point annotations.
Always review findings, especially cascade rounds and DI-risk rows, before deploying a destructive manifest.