Managing the lifecycle of your Salesforce code doesn't just involve creating new features; it also involves cleaning up technical debt. When you need to delete an Apex class using the Salesforce DX CLI, the process isn't always as straightforward as creating one. Depending on whether you are working in a Scratch Org, a Sandbox, or a Production environment, the commands and strategies you use will vary.
In this guide, we will walk through the most efficient ways to remove metadata from your orgs using the Salesforce CLI. Whether you prefer a direct command or need to handle complex dependencies via a manifest file, you will learn the exact steps to keep your codebase lean and efficient.
Using the Direct Source Delete Command
For most modern development workflows, the quickest way to remove a specific component from a non-scratch org (like a Sandbox or Developer Edition) is to use the built-in source delete functionality. This allows you to target a specific metadata member without manually crafting a XML manifest.
You can use the following command to delete an Apex class directly:
sfdx force:source:delete -m ApexClass:SomeClassName -r
Breaking Down the Command
-m ApexClass:SomeClassName: This flag specifies the metadata type and the specific name of the class you want to remove.-r: This stands for "remote." It tells the CLI to delete the component from the authenticated org as well as your local project.
This method is highly effective for quick removals, but keep in mind that it requires the CLI to be able to resolve all dependencies. If the class is referenced in a Profile or a Permission Set, the deletion might encounter errors.
Deleting Metadata in Scratch Orgs
Working with Scratch Orgs provides a much smoother experience because of Source Tracking. In a Scratch Org environment, you don't necessarily need a specific delete command for the remote server. Instead, the CLI monitors your local file system for changes.
To delete an Apex class in a Scratch Org, follow these steps:
- Delete the
.clsand.cls-meta.xmlfiles from your local project directory. - Run the push command:
sfdx force:source:push
Because the Scratch Org tracks the state of your local files, it recognizes that the files are missing and automatically performs a destructive deployment to match your local repository. This is the cleanest way to manage metadata during the active development phase.
The Destructive Changes Manifest Approach
Sometimes, the direct delete command isn't enough, especially when dealing with complex deployments or older CI/CD pipelines. In these cases, you should use the Metadata API approach with a destructiveChanges.xml file. This is the "tried and true" method for ensuring a clean removal across any org type.
Step 1: Create a Destructive Folder
Create a new folder in your project, for example, destructiveChanges.
Step 2: Create the Manifest Files
You will need two files in this folder: destructiveChanges.xml and a nearly empty package.xml.
destructiveChanges.xml This file lists the items you want to delete.
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>SomeTriggerToDelete</members>
<name>ApexTrigger</name>
</types>
<types>
<members>SomeClassToDelete</members>
<name>ApexClass</name>
</types>
</Package>
package.xml This file is required to satisfy the Metadata API requirements. It should reflect the API version of your project.
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<version>60.0</version>
</Package>
Step 3: Execute the Deployment
Once your files are ready, run the deployment command. Note that you must run a test class to satisfy Production deployment requirements.
sfdx force:mdapi:deploy -d destructiveChanges -u destinationOrgAlias -l RunSpecifiedTests -r SomeSimpleTestClassName -g -w -1
Key Flags Explained:
- -d: The directory containing your destructive manifests.
- -l RunSpecifiedTests: Ensures you meet the testing requirements for code changes.
- -g: (or --ignorewarnings) Skips errors if the metadata you are trying to delete doesn't actually exist in the target org.
Modernizing with the 'sf' CLI Syntax
Salesforce has transitioned from the sfdx executable to the unified sf CLI. If you are using the latest version of the toolset (Salesforce CLI v2), the syntax for performing destructive changes has been simplified into the project deploy command.
To perform a deletion using the modern sf syntax, use:
sf project deploy start -o destinationOrgAlias --manifest destructiveChanges/package.xml --post-destructive-changes destructiveChanges/destructiveChanges.xml --test-level RunSpecifiedTests --tests SomeSimpleTestClass
This command is more powerful as it allows you to define "pre" or "post" destructive changes, giving you granular control over whether the deletion happens before or after the rest of your code is deployed.
Handling Dependencies and Profile References
One common frustration when you delete an Apex class using the Salesforce DX CLI is the "Reference Error." Even if you delete the class, it may still be referenced in your local Profiles or Permission Sets. If you try to push or deploy, the validation will fail because the Profile is looking for a class that no longer exists.
The "Developer Console" Workaround
If you find yourself stuck in a loop where the CLI won't let you delete a class due to complex dependencies, try this hybrid approach:
- Open the Developer Console in the target org and delete the class manually.
- Immediately run a pull command to sync your local repository:
bash sfdx force:source:pull - This will remove the files locally and update your metadata to reflect the deletion, often bypassing the rigid dependency checks that occur during a CLI-initiated delete.
Frequently Asked Questions
Can I delete multiple Apex classes at once with one command?
Yes. If you use the force:source:delete command, you can provide a comma-separated list to the -m flag (e.g., -m ApexClass:ClassOne,ApexClass:ClassTwo). If using the manifest approach, simply add more <members> tags under the ApexClass type in your destructiveChanges.xml file.
Why does my deletion fail in Production but work in Sandbox?
Production environments require a minimum of 75% code coverage and the successful execution of tests. When deleting a class, you must still run tests (using -l RunSpecifiedTests) to ensure that removing that class doesn't break other functional areas of your application.
Does sfdx force:source:delete remove the file locally?
Yes, if you use the -r (remote) flag, the CLI will attempt to delete the metadata from the org and then remove the corresponding files from your local project directory.
Wrapping Up
Deleting an Apex class using the Salesforce DX CLI is a vital skill for maintaining a clean org. For rapid development in Scratch Orgs, simply deleting the file and using force:source:push is your best bet. For more formal environments, the force:source:delete command or the modern sf project deploy start with a destructive manifest provides the control and reliability needed for production-grade DevOps.
Always remember to check for references in Profiles and Permission Sets before attempting a deletion to avoid common deployment hurdles. With these tools in your belt, you can manage your Salesforce metadata with confidence and precision.