Salesforce destructive changes are metadata deletions packaged into a deployment through a destructiveChanges.xml file rather than removed manually through Setup. They exist because deleting a field, class, or flow through the UI does not travel with your deployment pipeline the way adding one does. If you build a picklist value in a sandbox and never delete it, that value stays orphaned in every downstream org forever unless someone deletes it deliberately in each environment.

Most admins learn about destructive changes the hard way, usually after a deployment that adds a new field succeeds while an old, redundant field sits untouched in production because nobody wrote the removal instruction anywhere. Metadata additions are additive by nature and forgiving. Deletions are not. Get a deletion wrong and you can break a report type, a flow, or a validation rule that nobody remembered was still pointing at the thing you just removed.

What Counts as a Destructive Change in Salesforce

A destructive change is any deployment operation that removes an existing metadata component from an org instead of creating or updating one. That includes custom fields, Apex classes and triggers, Visualforce pages, custom objects, record types, validation rules, and even entire permission sets. Salesforce handles these through a package.xml pair: destructiveChanges.xml lists what to delete, and it deploys alongside (or as part of) a standard package that may add or modify other components in the same operation.

The distinction matters because the Metadata API treats deletions as a separate operation from creation and updates. You cannot simply omit a component from your deployment package and expect Salesforce to infer that you want it gone. Absence is not a deletion instruction. If you want something removed from the target org, you have to say so explicitly, by name, in its own manifest file.

This explicit-declaration requirement is a safeguard, not an inconvenience. Salesforce will not guess what you meant to remove, and honestly, I would not want it to. An accidental omission from a package should never translate into an accidental deletion in production.

destructiveChangesPre.xml vs destructiveChangesPost.xml

Order of operations is where most destructive-change deployments go wrong. Salesforce gives you two files for a reason: destructiveChangesPre.xml runs before the main package deploys, and destructiveChangesPost.xml runs after. Choosing the wrong one for a given deletion is one of the most common causes of a failed or, worse, a silently broken deployment.

Use pre-deployment deletion when you're removing something that would otherwise conflict with new metadata in the same deploy. A classic case: renaming a custom field by deleting the old one and creating a new one with a different API name, where both would technically coexist and collide with a naming or validation rule during a mid-transaction state.

Use post-deployment deletion when the component you're removing is still referenced by other metadata being deployed in the same package, and that reference needs to be updated first. Deleting a field before removing its reference in a page layout or a flow, for instance, causes the whole deployment to fail on a dependency error rather than complete and clean itself up afterward.

ScenarioUse
Replacing a field with a same-purpose field under a new API namedestructiveChangesPre.xml
Removing a field once new flows/reports no longer reference itdestructiveChangesPost.xml
Deleting an unused Apex class with no incoming referencesdestructiveChangesPost.xml
Retiring a validation rule that would block new record types being addeddestructiveChangesPre.xml

Why Deletions Break More Than You Expect

Metadata in Salesforce is more interconnected than most org diagrams suggest. A single custom field can be referenced in a page layout, a report type, a flow, a validation rule, an approval process, and a formula field, sometimes all at once. Deleting that field does not just remove the field. It forces Salesforce to evaluate every dependent component, and if even one of those references is active, the deletion fails outright.

This is the same dependency web that causes the metadata errors covered in our dependency error guide, but destructive changes add a sharper edge: a failed addition just means nothing new gets created. A failed deletion, when it fails partway through a multi-component deployment, can leave an org in a partially cleaned state that's harder to reason about than if you had changed nothing at all.

Field-level references are the most common trap. Reports and report types quietly hold onto field references long after anyone remembers building them. A field that looks unused in the object manager might still be feeding a report that a VP checks every Monday morning. Deleting it without checking report type dependencies first is how deployment teams get angry calls on a Monday afternoon.

Testing Destructive Changes in Sandbox First

Never run a destructive change directly against production, full stop. Validate it in a sandbox that mirrors production's metadata state as closely as possible, ideally a partial or full copy sandbox refreshed recently enough that it reflects current field usage, not a developer sandbox that's six months stale and missing half your recent releases.

The gap between sandbox and production is exactly where destructive changes go sideways. A field deleted safely in a sandbox that never had the report built on it will fail, or worse succeed and silently orphan data relationships, when deployed to a production org where that report exists. This is one of the strongest arguments for keeping your sandbox environments genuinely in sync with production metadata rather than treating them as a rough approximation.

Run a validation-only deployment (check-only, no actual commit) against a full sandbox copy before you touch production. Validation-only deploys surface dependency conflicts without making any changes, giving you a clean list of exactly what would break before you find out the expensive way.

Common Destructive Change Failure Patterns

A handful of patterns account for the majority of destructive-change deployment failures we see across Salesforce teams. Knowing them ahead of time turns a deployment day surprise into a five-minute pre-check.

Every one of these produces a dependency error at deploy time rather than a silent failure, which is a small mercy. The real cost is time: manually tracing each dependency across flows, reports, layouts, and Apex takes hours in a metadata-dense org, and that's before you've deleted anything.

Automating Destructive Changes with DeployEzee

DeployEzee resolves metadata dependencies automatically before a destructive change ever reaches production, flagging every active reference to a component you've marked for deletion, whether that's a flow element, a report type, or a profile assignment. Instead of manually grepping through XML or clicking through Setup to check field usage, you get a dependency map before you commit to the delete.

The platform also sequences pre- and post-deployment deletions correctly based on the dependency graph it builds, removing the guesswork of which destructiveChanges file a given component belongs in. Combined with one-click deployment between sandbox and production, that means destructive changes get validated against real dependency data rather than a checklist someone maintains in a spreadsheet.

Deleting metadata safely is not about being cautious for its own sake. It's about treating deletions with the same rigor as additions, because an org that accumulates dead fields and orphaned classes for years eventually becomes one nobody wants to deploy into. Clean deletions, done in the right order and tested against real data, keep an org's metadata footprint honest.

Frequently Asked Questions

What is the difference between destructiveChangesPre.xml and destructiveChangesPost.xml?

destructiveChangesPre.xml deletes metadata before the rest of a deployment package runs, which matters when a deletion would otherwise conflict with new components in the same deploy. destructiveChangesPost.xml deletes metadata after the package runs, which is needed when the component being removed is still referenced by other metadata included in that same deployment. Choosing the wrong file is one of the most common causes of a failed destructive change deployment.

Can you undo a destructive change after it deploys to production?

There is no native undo for a destructive change once it commits to production. If the deleted metadata still exists in a source-controlled package or a recent sandbox backup, you can redeploy it manually, but any data or configuration tied to it that Salesforce discarded during deletion is not automatically recoverable. This is why validating destructive changes in a full or partial sandbox copy before touching production is not optional.

Why does a destructive change deployment fail with a dependency error?

Salesforce blocks a deletion if the component is still actively referenced by another piece of metadata, such as a flow, report type, validation rule, or page layout. The platform will not let you delete something out from under a dependent component without you addressing that reference first. Removing or updating the dependent reference before or within the same deployment resolves the error.

Do destructive changes need to be deployed separately from regular package changes?

No, destructive changes can be deployed alongside a standard metadata package in the same deployment operation, with the destructiveChanges.xml file included in the same zip as package.xml. Some teams choose to run destructive deployments separately anyway, purely to keep deployment logs and rollback scope cleaner. Either approach is valid as long as the pre and post ordering is set correctly.

How do you find every dependency before deleting a Salesforce field?

Salesforce's field usage tool in Setup shows some references, but it does not reliably surface every flow, Apex reference, or report type dependency, especially in metadata-heavy orgs. Running a validation-only (check-only) deployment against a full sandbox copy will surface dependency conflicts before they hit production. Tools like DeployEzee automate this by building a dependency map across flows, layouts, and code before a deletion is committed anywhere.