What still uses the class I am deleting?
You asked Salesforce to delete an Apex class. Something else still depends on it, so Salesforce refuses and tells you what. The message ends with the type and name of each dependent, such as a Visualforce page, a Visualforce component, another class, a trigger or a flow.
Salesforce will not let a deletion leave a broken reference behind. You have to remove the reference first.
Which things block an Apex class delete?
- A Visualforce page or component names the class as its controller or extension.
- Another Apex class or trigger calls the class. Test classes count.
- A flow calls the class as an invocable action, or an Aura component or LWC imports a method from it.
- A scheduled job or a custom button still points at it.
What does the delete error look like?
Deleting SiteLoginController, the class that backs the default site login pages, in a developer org. Destructive change, check-only:
SiteLoginController (ApexClass)
This apex class is referenced elsewhere in Salesforce. Remove the usage and try again. : Visualforce Page - Unauthorized.
This apex class is referenced elsewhere in Salesforce. Remove the usage and try again. : Visualforce Component - SiteLogin.
Notice the message does not stop at the first blocker. It names every dependent it found, so one run shows you the whole list.
How do I fix it in Setup?
- Read each dependent from the message and open it in Setup: Visualforce Pages, Visualforce Components, Apex Classes, Apex Triggers, Flows.
- Edit or delete each one so it no longer uses the class. If you are deleting a feature, delete the dependents too.
- Deploy that change first, or send the edit and the delete together (see the next paragraph).
- Run the delete as a check-only deploy again. Repeat until it validates.
- Run it for real.
The Metadata API lets you order the deletion. A file named destructiveChangesPre.xml deletes before components are added or updated. destructiveChangesPost.xml deletes after. Salesforce uses nearly this case as its example: when an Apex class references the thing you delete, one deploy can update the class and then delete the other component (Metadata API Developer Guide). For your case, put the edited dependents in the package and the old class in the post file:
How do I fix it with the CLI?
sf project deploy start --dry-run --manifest package.xml --post-destructive-changes destructiveChanges.xml -o <target-alias>
Wildcards are not supported in destructiveChanges.xml, so name each class (Metadata API Developer Guide). The same guide notes you cannot delete items associated with an active Lightning page this way. Remove the page's action override first.
How do I check dependencies before a class delete?
Before deleting any class, search the project for its name: grep -r "SiteLoginController" force-app. That catches code references, but not flows or Visualforce in other orgs, so also run the check-only deploy against the real target. Delete old test classes together with the class they test.
A sandbox that has already deleted the class can mislead you. The sandbox has no blockers left, and production still has them.
Clientell AI takes a snapshot before every deploy, so a class delete you regret can be rolled back. See AI Salesforce deployments. The change set errors guide covers deletes in change sets.
The other delete error: deleting a field that is still used.
Questions about deleting a referenced Apex class
Can I delete the class with a change set?
No. Change sets cannot delete components. Use a destructive change or delete it in the org after you remove the references.
The class is only used in a test. Why does it still block?
A test class is Apex code like any other. Delete the test with the class, in the same destructive change.
What if the dependent is in a managed package?
You cannot edit it. Remove the dependency on your side if you can. If not, keep the class.
Can I delete SiteLoginController?
Only after you remove the site login pages that use it as a controller.