PMD

PMD

Category: SAST
License: Free/OSS

PMD is an open-source static analysis tool that scans source code for common programming flaws, including potential bugs, dead code, suboptimal code, and security vulnerabilities.

With over 5,300 GitHub stars, 312 contributors, and more than 30,000 commits, PMD represents one of the most mature and actively maintained code analysis projects in the Java ecosystem.

Originally created for Java, PMD now supports multiple languages and includes CPD (Copy/Paste Detector), a tool for finding duplicated code across large codebases.

What is PMD?

PMD analyzes source code without executing it, applying configurable rules to identify problematic patterns.

The tool earned its reputation in the Java community for catching issues that compilers miss but that lead to bugs, maintainability problems, or security vulnerabilities in production.

The name PMD does not officially stand for anything, though the community has proposed various backronyms including “Programming Mistake Detector” and “Pretty Much Done.” What matters is its effectiveness: PMD ships with over 400 built-in rules covering code style, design issues, potential bugs, and security concerns.

PMD is used by Salesforce’s Code Analyzer for Apex development, making it the de facto standard for Salesforce security and code quality analysis.

The tool also integrates with SonarQube and other code quality platforms, serving as a rule engine within larger analysis workflows.

Key Features

Comprehensive Rule Library

PMD includes hundreds of rules organized by category:

  • Best Practices: Identifies code that works but could be written better
  • Code Style: Enforces consistent formatting and naming conventions
  • Design: Detects structural issues like excessive class complexity
  • Documentation: Checks for missing or inadequate comments
  • Error Prone: Catches patterns that commonly lead to bugs
  • Performance: Identifies inefficient code patterns
  • Security: Detects potential security vulnerabilities

Rules can be enabled or disabled individually, and severity levels can be customized to match organizational standards.

Copy/Paste Detector (CPD)

CPD finds duplicated code across files and even across different languages.

Code duplication is a significant maintainability concern because bugs fixed in one location often remain unfixed in copies.

CPD supports:

  • Java, C/C++, C#, Go, Kotlin, Ruby, Swift, and many more
  • Configurable minimum token threshold
  • Ignoring comments and literals
  • Multiple output formats for integration

Custom Rule Development

PMD allows creating custom rules through multiple approaches:

  • XPath rules: Write rules using XPath expressions against the AST (Abstract Syntax Tree)
  • Java rules: Implement complex logic in Java for sophisticated analysis
  • Rule references: Compose rules from existing rules with modified properties

This extensibility allows organizations to encode their specific coding standards into automated checks.

Incremental Analysis

For large codebases, PMD supports incremental analysis that only processes changed files.

This dramatically reduces analysis time in CI/CD pipelines where most builds only modify a few files.

Installation

Command Line Installation

# Download latest release
curl -LO https://github.com/pmd/pmd/releases/download/pmd_releases%2F7.21.0/pmd-dist-7.21.0-bin.zip
unzip pmd-dist-7.21.0-bin.zip
cd pmd-bin-7.21.0

# Add to PATH (bash/zsh)
export PATH=$PATH:$(pwd)/bin

# Verify installation
pmd --version

Package Managers

# macOS via Homebrew
brew install pmd

# Linux via SDKMAN
sdk install pmd

Running PMD

# Basic analysis
pmd check -d /path/to/source -R rulesets/java/quickstart.xml -f text

# With specific ruleset
pmd check \
  --dir ./src/main/java \
  --rulesets rulesets/java/quickstart.xml,category/java/security.xml \
  --format html \
  --report-file pmd-report.html

# Using CPD for duplicate detection
pmd cpd \
  --dir ./src \
  --minimum-tokens 100 \
  --language java \
  --format xml > cpd-report.xml

# Incremental analysis with cache
pmd check \
  --dir ./src \
  --rulesets rulesets/java/quickstart.xml \
  --cache pmd-cache.bin

Maven Integration

<!-- pom.xml -->
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-pmd-plugin</artifactId>
      <version>3.21.0</version>
      <configuration>
        <rulesets>
          <ruleset>/rulesets/java/quickstart.xml</ruleset>
          <ruleset>/category/java/security.xml</ruleset>
        </rulesets>
        <failOnViolation>true</failOnViolation>
        <printFailingErrors>true</printFailingErrors>
      </configuration>
      <executions>
        <execution>
          <goals>
            <goal>check</goal>
            <goal>cpd-check</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>
# Run PMD via Maven
mvn pmd:check
mvn pmd:cpd-check

Gradle Integration

// build.gradle
plugins {
    id 'pmd'
}

pmd {
    toolVersion = '7.21.0'
    consoleOutput = true
    ruleSetFiles = files('config/pmd/ruleset.xml')
    ruleSets = []  // Disable default rulesets
}

tasks.withType(Pmd) {
    reports {
        xml.required = true
        html.required = true
    }
}
# Run PMD via Gradle
./gradlew pmdMain pmdTest

Integration

GitHub Actions

name: PMD Analysis
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  pmd:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup PMD
        run: |
          curl -LO https://github.com/pmd/pmd/releases/download/pmd_releases%2F7.21.0/pmd-dist-7.21.0-bin.zip
          unzip pmd-dist-7.21.0-bin.zip

      - name: Run PMD
        run: |
          ./pmd-bin-7.21.0/bin/pmd check \
            --dir ./src \
            --rulesets rulesets/java/quickstart.xml \
            --format sarif \
            --report-file pmd-results.sarif

      - name: Upload SARIF results
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: pmd-results.sarif

      - name: Run CPD
        run: |
          ./pmd-bin-7.21.0/bin/pmd cpd \
            --dir ./src \
            --minimum-tokens 100 \
            --language java \
            --format text

GitLab CI

stages:
  - analysis

pmd:
  stage: analysis
  image: openjdk:17-jdk
  before_script:
    - apt-get update && apt-get install -y curl unzip
    - curl -LO https://github.com/pmd/pmd/releases/download/pmd_releases%2F7.21.0/pmd-dist-7.21.0-bin.zip
    - unzip pmd-dist-7.21.0-bin.zip
  script:
    - ./pmd-bin-7.21.0/bin/pmd check
        --dir ./src
        --rulesets rulesets/java/quickstart.xml
        --format xml
        --report-file pmd-report.xml
  artifacts:
    reports:
      codequality: pmd-report.xml
    paths:
      - pmd-report.xml
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == "main"

Custom Ruleset

Create a custom ruleset to encode your organization’s standards:

<?xml version="1.0"?>
<ruleset name="Custom Rules"
         xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0
                             https://pmd.sourceforge.io/ruleset_2_0_0.xsd">

    <description>Custom PMD ruleset for our project</description>

    <!-- Include security rules -->
    <rule ref="category/java/security.xml"/>

    <!-- Include specific best practices -->
    <rule ref="category/java/bestpractices.xml/AvoidReassigningParameters"/>
    <rule ref="category/java/bestpractices.xml/UnusedPrivateField"/>

    <!-- Customize rule properties -->
    <rule ref="category/java/design.xml/CyclomaticComplexity">
        <properties>
            <property name="classReportLevel" value="80"/>
            <property name="methodReportLevel" value="15"/>
        </properties>
    </rule>

    <!-- Exclude specific rules -->
    <rule ref="category/java/codestyle.xml">
        <exclude name="AtLeastOneConstructor"/>
        <exclude name="OnlyOneReturn"/>
    </rule>
</ruleset>

When to Use PMD

PMD is an excellent choice for:

  • Java projects seeking comprehensive code quality and security analysis without licensing costs
  • Salesforce development where PMD powers the official Code Analyzer for Apex
  • Teams needing duplicate detection across large codebases with CPD
  • Organizations building custom rules who need extensibility through XPath or Java
  • CI/CD pipelines that benefit from incremental analysis and fast execution

PMD may be less suitable when deep security analysis is the primary goal (consider Semgrep, CodeQL, or commercial SAST tools), when analyzing languages not well-supported by PMD, or when a managed SaaS experience is preferred over self-hosted tooling.

PMD works well alongside other tools in a defense-in-depth strategy.

Many teams run PMD for code quality and style, then layer specialized security scanners for vulnerability detection.