One of the key requirements to developing good, maintainable software is to ensure that it works under a variety of conditions. This is typically done by automating a suite of tests on the various features and code paths your application can take. While unit tests are excellent for making sure that your application technically runs, there’s another category of verification that ensures that your application has no other detectable issues: static analysis. If you’ve ever worked with a compiled language like Kotlin, the compiler implements one form of static analysis by ensuring that your program adheres to the grammatical rules of the language. For example, if you call a function but forget to pass in required arguments, a static analyzer alerts you to this error before you compile your application. This is in contrast to an interpreted language such as JavaScript, in which the error would occur when executing the code because there’s no compiler to anticipate the issue. Static analysis is a method of analyzing your code executing it. without (To be technically precise, a static analyzer be applied to interpreted languages like JavaScript, Ruby, or Python, ensuring that the code is well-formatted and has no missing logic.) can Benefits of Static Analysis While a well-written test suite is likely to cover such code paths, static analysis can do so much more. A static analyzer can reduce the possibility of bugs, such as when you accidentally overwrite a variable with another value. It can also implement linting and formatting rules, which make your codebase consistent and easier to review. Some static analyzers even bring performance benefits by suggesting ways to rewrite loops or other functional calls. Nearly every programming language has a static analyzer of its own. For example, golang has , which is baked into the standard tooling, while Ruby has , a community-led project. Even compiled languages like C have their own static analyzer through . However, it can be difficult (and tedious) to run several analyzers across polyglot projects. Fortunately, that’s where a project like can be of assistance. PMD is a static analyzer that allows you to define a standard set of rules that can be applied over multiple languages. gofmt Rubocop astyle PMD 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 several issues that PMD can report and act on. We’ll also integrate PMD into your editor, as well as your CI environment, to make sure that your build will fail if the static analysis detects any problems. Prerequisites Before getting started, you should have some familiarity with , Salesforce’s programming language. We’ll be using along with . You’ll also need the , which is a tool designed by Salesforce to simplify interacting with the platform. Apex VS Code the Apex plugin Salesforce CLI Next, go ahead and follow the . installation instructions for PMD Finally, clone our sample Apex project at this Git repository: https://github.com/gjtorikian/sfdc-linting.git This repository is a forked copy of the , except it has (intentionally) introduced some errors. We’ll use this to demonstrate how PMD works. dreamhouse-lwc project Integrating PMD First, . If you don’t have a Salesforce instance, no worries. You can , which is like a temporary Salesforce org. You can use the scratch org to test what developing on the Salesforce platform looks like. enable Dev Hub for your Salesforce organization create a scratch org Whether you’re using a scratch org or your own, you’ll need to associate sfdx with the org by logging in. Run the following command to do so: sfdx auth:web:login This will open a new browser window that will ask for your Salesforce credentials. When that’s finished, the Salesforce CLI will inform you when the authentication is complete. Now, let’s see what happens when we try to upload the cloned project to our org. Navigate to the directory you cloned the project to, and run the following command: dreamhouse-sfdx 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! It looks like there were several issues in this file, stemming from missing semicolons. (How’d that get past code review?) Open up in VS Code, and add a semicolon to the end of the statements on lines 6 and 12. That should solve the problem, right? force-app/main/default/classes/PagedResult.cls Well...perhaps not. While our code compiles and has no build errors, our project may have some other issues we’re not aware of. On the command line, type the following command: pmd -d . -R config/ruleset.xml Here, we’re running PMD on the current directory ( ), and we’re applying a ruleset located at . When you execute this command, you’ll see dozens of lines that look like this: -d . config/ruleset.xml 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 ruleset, PMD identified several issues in our project: We’re missing the logging severity level. We’re using global modifiers, which could have unwanted side effects. We’re creating unused local variables, which is a waste of memory and time. Open up the file, and you’ll find an XML document that lists several . These rules map to the issues which PMD will report on. Believe it or not, there are of Apex rules, and you can find the full set at the . You have complete control over which rules to enable. Typically, you’d determine which ones are important by agreeing with your teammates on the ones that matter most. After all, their code will be statically analyzed, too! config/ruleset.xml rules hundreds PMD repo Integrating PMD in VS Code Switching to the command line to statically analyze your code can get a bit tedious, if not outright disruptive to your workflow. Since static analysis looks at the structure of your code without compiling it, you can integrate tools like PMD directly into your editor. That means you can get feedback on your code as you’re writing it. Fortunately, several plugins allow you to integrate PMD into VS Code. Let’s install one and see what that process looks like. Visit the on the VS Code Marketplace and click . This downloads the plugin and installs it into your editor—but we’re not finished just yet. Apex PMD extension homepage Install The Apex PMD extension comes with its own ruleset which, while convenient, may not be the same rules you’ve established for your project. We’ll need to configure the tool to point to our predetermined ruleset. Open up the VS Code settings page (this can be found in the menu bar under ), and type to filter the settings to just that extension. Then, in the section, set the path to the file we created in this project. Code > Preferences > Settings pmd Rulesets ruleset.xml Next, navigate to any file in your project. You’ll see varying squiggly lines indicating the issues found by PMD. Hovering over these lines also presents a dialog box indicating what the issue is, as well as which rule triggered it. .cls Integrating PMD in CI Having PMD run while writing your code is a good step towards catching issues before they enter production. However, the very way to do this is to set up your PMD analysis as part of the test suite in your CI/CD pipeline. best The first step is to install PMD on your CI servers. You can do this by grabbing a containerized version of the program, or . by downloading the package with wget and unzipping it If you’re using GitHub Actions, you can just integrate , which takes care of all the installation and configuration for you. If you’re not, you simply need to run PMD as you do on the CLI, within a script: an action such as this one #!/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 a failure, too, if there are any issues. Conclusion Static analysis not only helps keep your code consistent and clean, but it can also help make it more efficient by pointing out small inconsistencies that could add up to bigger problems. It should be an important tool in your toolbox. In addition, by integrating static analysis with your CI tests, you can rest assured that once your code is fixed, it stays fixed. To learn more about how to write better Apex code, Salesforce has some with some advanced topics. also details all of the available Apex rules you can use. Salesforce also has that can be installed as plugins to make writing code much easier! Trailhead badges The PMD documentation a whole suite of tools First published here