One of the main requirements for developing good, maintainable software is to ensure that it works under a variety of conditions. This is usually done by automating a set of tests on the different features and code paths your application can take. While unit tests are excellent for making sure your application is technically running, there is another class of validation which ensures that your application doesn’t have other detectable issues: static analysis.
Benefits of static analysis
While a well-written test suite will likely cover these code paths, static analysis can do much more than that. The static parser can reduce the possibility of errors, such as when you accidentally overwrite a variable with another value. It can also implement checking and formatting rules, which makes your code base consistent and easy to review. Some static parsers even bring performance advantages by suggesting ways to rewrite loops or other function calls.
Almost every programming language has its own static parser. For example, golang has gofmt, which is baked into standard tools, while Ruby has Rubocop, a community-led project. Even compiled languages like C have their own static parser through astyle. However, it can be difficult (and tedious) to run multiple analyzers across multilingual projects. Fortunately, this is where a project like PMD can come in handy. PMD is a static parser that allows you to define a standard set of rules that can be applied to multiple languages.
In this post, we’ll take a closer look at PMD, and learn how to run it on Apex code. Our Apex project will have many issues that PMD can report and act on. We will also integrate PMD into your editor, as well as your CI environment, to ensure that your design will fail if static analysis detects any problems.
Before getting started, you should have some familiarity with Apex, the Salesforce programming language. We will be using VS Code along with the Apex plugin. You will also need the Salesforce CLI, a tool designed by Salesforce to simplify interaction with the platform.
Next, go ahead and follow the PMD installation instructions.
Finally, clone our sample Apex project in this Git repository: https://github.com/gjtorikian/sfdc-linting.git
This repository is a forked version of the Dreamhouse-lwc project, except that it (intentionally) introduced some errors. We will use this to explain how PMD works.
First, enable the Dev Hub for your Salesforce organization. If you don’t have a Salesforce instance, don’t worry. You can create a scratch organization, such as the temporary Salesforce Foundation. You can use the Scratch Foundation to test what development on the Salesforce platform looks like.
Whether you’re using a scrat organization or your own organization, you’ll need to link sfdx to your organization by logging in. Run the following command to do this:
This will open a new browser window asking for your Salesforce credentials. When it’s done, the Salesforce CLI will tell you when authentication is complete.
Now, let’s see what happens when we try to upload the cloned project to our organization. Go to the directory you cloned
dreamhouse-sfdx project to and run the following command:
sfdx force:source:push -u <admin_email_address>
You should see the following output:
*** Deploying with SOAP *** Job ID | 0AfR000001XgjR1KAJ SOURCE PROGRESS | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | 0/2 Components TYPE PROJECT PATH PROBLEM ───── ──────────────────────────────────────────────── ────────────────────────────────────────────── Error force-app/main/default/classes/PagedResult.cls Unexpected token '}'. (12:40) Error force-app/main/default/classes/PagedResult.cls Unexpected token 'set'. (6:37) ERROR running force:source:push: Push failed.
Uh oh! There seem to be several issues with this file, caused by the lack of a semicolon. (How did that bypass the code review?)
force-app/main/default/classes/PagedResult.cls In VS Code, and add a semicolon to the end of the statements on lines 6 and 12. This should solve the problem, right?
Well…maybe not. Although our code compiles and does not contain build errors, our project may contain some other issues that we are not aware of. On the command line, type the following command:
pmd -d . -R config/ruleset.xml
Here, we are running PMD in the current directory (
-d .), and we apply a set of rules found in
config/ruleset.xml. When you execute this command, you will see dozens of lines that look like this:
main/default/classes/PostPriceChangeToSlackTest.cls:11: DebugsShouldUseLoggingLevel: Calls to System.debug should specify a logging level. main/default/classes/PropertyController.cls:1: AvoidGlobalModifier: Avoid using global modifier main/default/classes/SampleDataController.cls:20: UnusedLocalVariable: Variable 'brokersJSON' defined but not used
This is the power of PMD. According to our rules, PMD has identified several issues in our project:
- We’re missing the logging severity level.
- We use global rates, which may have undesirable side effects.
- We are making unused local variables, which are a waste of memory and time.
config/ruleset.xml file, and you will find an XML document that lists many files
rules. These rules define the issues that the PMD will report on. Believe it or not, there hundreds for Apex bases, and you can find the full set in the PMD repository. You have complete control over which rules must be enabled. Usually, you can identify the important elements by agreeing with your teammates on the people who are most important. After all, their code will be parsed statically too!
PMD integration into VS Code
Switching to the command line to statically analyze your code can become a bit tedious, if not completely disruptive to your workflow. Since static analysis looks at your code structure without compiling it, you can integrate tools like PMD directly into your editor. This means that you can get feedback on your code as you write it.
Fortunately, many plugins allow you to integrate PMD into VS Code. Let’s install one and see what the process looks like. Visit Apex PMD Extension homepage on VS Code Marketplace and click Installations. This downloads and installs the plugin in your editor – but we’re not done yet.
The Apex PMD extension comes with its own set of rules which, while convenient, may not be the same as the rules you created for your project. We will need to configure the tool to point to the predefined set of rules.
Open the VS Code Settings page (can be found in the menu bar under Code > Preferences > Settings) And type
pmd To filter settings for this extension only. then in the rules section, set the path to
ruleset.xml A file we created in this project.
After that, go to any
.cls file in your project. You will see different squiggly lines indicating issues found by PMD. Hovering over these lines also brings up a dialog indicating the problem, as well as the rule that triggered it.
PMD integration into CI
Running a PMD while code is being written is a good step toward spotting problems before they go into production. However, the very Best The way to do this is to set up your PMD analysis as part of the testing suite in your CI/CD pipeline.
The first step is to install PMD on your CI servers. You can do this by getting a container copy of the program, or by downloading the package with wget and decompressing it.
If you use GitHub Actions, you can just incorporate an Action like this, which takes care of all your installation and configuration. If you are not, you simply need to run the PMD as you would in a CLI, inside a script:
#!/bin/sh set -e pmd -d . -R config/ruleset.xml
Since PMD fails with a non-zero status, running this script will mark your CI as failing as well, if there are any issues.
Not only does static analysis help keep your code consistent and clean, but it can also help make it more efficient by pointing out small inconsistencies that can lead to bigger problems. It should be an important tool in your toolbox. Plus, by integrating static analysis with your CI tests, you can rest assured that once you fix your code, it stays static.
To learn more about how to write better Apex code, Salesforce has some Trailhead badges with some advanced topics. The PMD documentation also explains all the available Apex bases that you can use. Salesforce also has a whole suite of tools that can be installed as plugins to make writing code much easier!