Removing Deprecated Metadata Using The Salesforce CLI

Ángel Alberto Mata
5 min readJun 3, 2021

When we start working in an old infrastructure, we need to figure out the components that source our features or (possibly) bugs. If the entire codebase has been migrated to a VCS, we have an advantage because it’s easier to understand the dependency tree of every functionality. However, these repositories are repleted by old metadata that is no longer needed but keeps affecting our development cycle. Wouldn’t it be necessary to execute a process to remove these components from our organization? Well, thanks to the Salesforce CLI, now that’s possible.

The weight of legacy metadata

There are a couple of reasons to remove deprecated components from our instances. The first one is that these components tend to increase debugging time. Let’s suppose we have to apply a critical update for Apex. To find all the affected classes for this update, we will encounter code that is not functional anymore. However, we will spend a couple of hours discarding them from the code refactoring, and these scenarios will be more common as the codebase grows.

The second reason is that old components will affect our CI integration. Let’s suppose we have a Lightning controller written in Apex designed to update the account object. If the controller gets deprecated, we are not going to support this code in subsequent sprints. However, if we add some new mandatory fields to the account object and the unit test for the controller does not contain this definition, it will break our deployments. In this case, either we remove the controller class and its test from our organization or keep updating them.

Nevertheless, our dependency tree for deprecated code is way more complex than that. It includes summary fields, Visualforce pages, and triggers, and we cannot even deactivate triggers in production environments using declarative tools. Keep in mind that it would be a questionable practice to remove the classes only from our repositories. So, what is needed to keep our codebase clean?

Track everything in your organization

The first step to ease our deployments is to track all the components in our Salesforce instances. The right way to do it is to migrate all your metadata to a VCS, like git. This way, the dependency tree will be more readable for all our team members, and you can easily keep an eye on how your changes affect other projects.

This principle also applies to deprecated metadata. If we track the dependencies between all these components, we will know how to remove them from our organization. As you may notice, if you version your projects initially, identifying deprecated metadata is reduced to the simple task of taking a subset of these components.

Deploying destructive changes

Creating Salesforce DX projects and tracking them using git are topics that would take another two articles. However, many documents cover these subjects. In this case, we will take for granted the use of these tools and focus on how to remove metadata components from our Salesforce environments using the CLI.

The first thing that we need to understand is that deleting metadata is not different from deploying it. That’s because some options of the Salesforce CLI are mapped directly to the Metadata API. The API has a method that is identical to the one that we are going to use here, but sfdx has the advantage that we can integrate it into a shell script.

1. Creating a new Salesforce DX project

Although it’s unnecessary to retrieve the metadata we will delete, it’s a good practice to create a backup. If we already installed the sfdx-cli package in our local environments, we can invoke the sfdx command to execute all the tasks. Let’s first create a project directory called TestingComponentsDeletion:

sfdx force:project:create --projectname TestingComponentsDeletion

The directory structure should be something similar to this:

TestingComponentsDeletion
├── README.md
├── config
│ └── project-scratch-def.json
├── force-app
│ └── main
│ └── default
│ ├── applications
│ ├── aura
│ ├── classes
│ ├── contentassets
│ ├── flexipages
│ ├── layouts
│ ├── lwc
│ ├── objects
│ ├── permissionsets
│ ├── staticresources
│ ├── tabs
│ └── triggers
├── jest.config.js
├── package.json
├── scripts
│ ├── apex
│ │ └── hello.apex
│ └── soql
│ └── account.soql
└── sfdx-project.json

2. Fetching deprecated metadata

Either you can fetch your components from your instance or copy them from other repositories you have. Let’s suppose we need to remove four objects; an object called TestingObject__c; a field ExtraField__c that is member of an object we want to preserve called ExtraObject__c; and two classes that depend on these objects, TestingObjectController and TestingObjectControllerTest. First, we need to fetch them from our instances with the following command:

sfdx force:source:retrieve --metadata \
ApexClass:TestingObjectController,\
ApexClass:TestingObjectControllerTest,\
CustomObject:TestingObject__c,\
CustomField:ExtraObject__c.ExtraField__c

As you may notice, you need to put the metadata type before every component’s name. But is more important to note that it’s possible to retrieve even individual fields without affecting the object definition or other fields.

3. Listing components on manifest files

We need two create two manifest files just as the official documentation specifies. Let’s first make a directory called deprecationManifests. Then we will add a package.xml file containing only the API version for our deployment and another file called destructiveChanges.xml, where our components will be listed.

deprecationManifests

deprecationManifests
├── destructiveChanges.xml
└── package.xml

package.xml

<?xml version=”1.0" encoding=”UTF-8" standalone=”yes”?>
<Package xmlns=”http://soap.sforce.com/2006/04/metadata">
<version>51.0</version>
</Package>

destructiveChanges.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>TestingObjectController</members>
<members>TestingObjectControllerTest</members>
<name>ApexClass</name>
</types>
<types>
<members>ExtraObject__c.ExtraField__c</members>
<name>CustomField</name>
</types>
<types>
<members>TestingObject__c</members>
<name>CustomObject</name>
</types>
<version>51.0</version>
</Package>

You can always go to the Metadata API documentation to read the proper way to list your components.

4. Deploying changes

Once we have everything set up, the last part is to deploy the manifest files to our environment:

sfdx force:mdapi:deploy --deploydir deprecationManifests \
--targetusername developer-org \
--testlevel RunSpecifiedTests \
--runtests MOCKED -w 3
Deploying destructive changes

Since the sfdx command does not allow us to deploy anything to production environments without specifying a test run, we added a placeholder string MOCKED to satisfy the command requirements. However, you can use any word you want. The reasons why that workaround does its job are not evident, but we can assume that since we are not creating or updating any Apex class, the API does not check for code coverage. Yet, I have tested this feature only on versions sfdx-cli/7.103.0 darwin-x64 node-v14.17.0 and sfdx-cli/7.99.0 wsl-x64 node-v12.22.0.

However, don’t forget that the ideal scenario is to execute all tests in the organization after every deployment, and that’s because Salesforce enforces a test-driven development methodology. If our deployment affects code coverage, there is a sign that we probably are missing something. The capacity to execute all tests in our organization at once will lead us to a seamless continuous integration process.

References

I created a repository with the components listed in this article if you want an out-of-the-box testing experience:

--

--

Ángel Alberto Mata

Salesforce Developer. Interested in open source projects and the integration of new tools that accelerate the development of Salesforce applications.