
Serpent Team

Andrew Hanna

You delete metadata in Salesforce with a destructiveChanges.xml manifest
deployed alongside a package.xml, choosing whether the deletions run
before or after the additive part of the deployment. The dangerous part is not the
syntax. It is that a successful deletion is not something a deployment rollback can
undo, and that the components most likely to break are the ones nobody has in source
control.
A
destructiveChanges manifest
uses the same format as package.xml, with one important difference:
wildcards are not supported. Every component you intend to remove
must be named.
A minimal entry looks like
<types><members>Account.Legacy_Score__c</members><name>CustomField</name></types>.
Two rules that trip people up on the first attempt:
package.xml must still be present, in the same directory. For a
delete-only deployment it carries the API version and lists no components.
The Salesforce CLI gives you both, through
two separate manifests: --pre-destructive-changes deletes before the deploy,
--post-destructive-changes deletes after it.
Use post-destructive by default. The additive package lands first, so anything that still references the doomed component can be repointed in the same deployment before it disappears.
Use pre-destructive when the old and the new collide. The clearest cases:
Getting this backwards produces the two classic failures: a deletion that fails because something still references the component, or an addition that fails because the name is taken.
This is the part worth being blunt about. Deleting a field deletes its data. Deleted components go to the Recycle Bin unless the deployment sets purgeOnDelete, which makes them immediately eligible for deletion instead. Roll-up summary fields bypass the Recycle Bin regardless of that setting.
And rollbackOnError, which is mandatory for production deployments,
reverses the changes of a failed deployment. It is not an undo button for a
deletion that succeeded. If you need the data back after a successful purge, you are
in a restore conversation, not a deployment one.
There is also a category of breakage that never shows up in your repository: reports, list views and dashboards that reference the field. They are configuration a business user built, they are usually not in Git, and the deployment will not warn you about them.
checkOnly deployment,
or --dry-run from the CLI, validates and runs tests without saving
anything.
The safe pattern is not a better manifest. It is splitting the deletion into two releases.
Shipping a deletion alone matters. If it is bundled with twenty other changes and something goes wrong, you cannot isolate the cause quickly, and rollback takes the good changes with it.
The same discipline applies to renames, which Git records as a delete plus an add. Treat every rename as a two-release deprecation unless you can prove nothing references the old name. More delivery playbooks are in our SF Guides library.
Can I use a wildcard in destructiveChanges.xml?
No. Wildcards are not supported. Every component to be deleted has to be named explicitly.
Do I still need a package.xml when I am only deleting?
Yes. It must sit in the same directory, carry the API version, and list no components.
Can I recover a field deleted by a deployment?
It goes to the Recycle Bin unless purgeOnDelete was set, and roll-up
summary fields bypass the bin regardless. Treat a data export as the real recovery
plan.
Why did my destructive deployment succeed but delete nothing?
Deletion attempts continue when a listed component is absent from the target, so a wrong API name produces a green deployment and no change. Diff the target before and after.
When should deletions run before the deploy instead of after?
When the old and new components conflict: reusing an API name, recreating a field with a different type, or removing a rule that would block the rest of the deployment.
Commitment free!