Writing Clean and Consistent Code with Static Analysis using PMD and Apex

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.

Static analysis is a way to analyze your code without its implementation. If you’ve ever worked in an assembly language like Kotlin, the compiler performs a single form of static parsing by making sure that your program adheres to the language’s grammar. For example, if you call a function but forget to pass the required arguments, a static analyzer alerts you to this error before compiling your application. This is in contrast to an interpreted language like JavaScript, where the error will occur when executing the code because there is no compiler to anticipate the problem.

(To be technically accurate, a static parser can It is applied to interpreted languages ​​such as JavaScript, Ruby, or Python, ensuring that the code is well-formed and does not contain missing logic.)

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.

Basic requirements

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.

PMD Integration

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:

sfdx auth:web:login

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?)

Open 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.

open file 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:

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!


Leave a Comment