If you use PMD to check your Apex code, you may see warnings that do not always need immediate action. Some warnings highlight real problems, while others may not apply to your specific code or situation. This guide helps you understand the difference and helps you suppress PMD warnings.
You will learn what PMD checks, how to understand a violation, when you should suppress a warning, and the correct syntax for suppressing PMD warnings in Apex. You will also learn how to use suppressions carefully without turning them into a place where unreviewed risks get ignored.
What Is PMD in Salesforce Apex?
PMD is a static code analysis tool. It reads your Apex source without running it and flags patterns that match a set of predefined rules. It doesn’t know your business logic, and it doesn’t execute a single line. It is a pattern match against known problem shapes: SOQL inside a loop, a missing sharing declaration, a class that’s grown too large, and so on.
Salesforce teams usually run PMD through the Salesforce Extensions for VS Code, the standalone PMD CLI, or a CI pipeline step. The rules live in an XML ruleset, grouped into categories like security, performance, best practices, design, and code style. Each rule has a name, a severity, and a page in the official PMD documentation that explains why the rule exists and how to fix a violation.
PMD isn’t unique to Salesforce; it’s a general-purpose Java/Apex/JS analyzer, but its Apex-specific rules are tuned for Salesforce patterns: governor limits, sharing rules, CRUD/FLS checks, and trigger design.
Read Guide: Best Code Analysis Tools For Salesforce Development
Why Does PMD Report Warnings?
PMD reports a warning any time your code matches a rule’s pattern, regardless of whether that pattern is actually a problem in context. A few common reasons a warning shows up:
- The pattern is genuinely risky. A SOQL query built with string concatenation from user input really can be an injection vector.
- The pattern usually causes a governor limit issue. A SOQL query inside a
forloop can push you toward the 100-query limit once real data volume hits it, even if it works fine with two test records. - The rule is a style or maintainability guideline, not a correctness bug—excessive class length, excessive parameter count, deeply nested
ifstatements. - The rule doesn’t understand your specific context. PMD can’t tell that your loop only ever iterates over a hard-business-capped list of five records, or that a particular
catchblock is intentionally empty because the calling code already handles the failure path.
How to Identify a PMD Rule
Before you decide to fix or suppress anything, you need to know exactly which rule fired and why.
Reading a PMD violation
A typical PMD output line looks like this:
AccountTriggerHandler.cls:42: Avoid using SOQL/SOSL/DML inside for loops.
Rule: AvoidSOQLInLoops Priority: 3 Ruleset: Performance
Let us break it down:
- File and line number — where the pattern was found.
- Message — a short, human-readable description of the issue.
- Rule name—the exact identifier you’ll use if you suppress it (e.g.
AvoidSOQLInLoops). - Priority — PMD’s severity scale, typically 1 (highest) to 5 (lowest). Many teams fail their build only on priority 1–2 violations.
- Ruleset—the rule category (Security, Performance, Best Practices, Design, Code Style, Error Prone, Documentation).
Finding the rule documentation
Every Apex rule has a page under the PMD project’s Apex rule reference, organized by ruleset (pmd_rules_apex_security.html, pmd_rules_apex_performance.html, and so on). Each page explains:
- what the rule detects,
- why it matters,
- a “good” and “bad” code example,
- and sometimes rule-specific properties you can tune instead of suppressing outright.
Before suppressing anything, read the rule’s page. Some rules, like ExcessiveParameterList have configurable thresholds. If your team’s real limit is 5 parameters and the default is 4, changing the ruleset property fixes every future false positive, instead of suppressing them one by one.
Should You Fix or Suppress a PMD Warning?
This is an important decision. It is easy to suppress a PMD warning, but you should not suppress warnings without thinking about the risk. Instead of making a decision based only on your judgment at that moment, follow a simple process.
Decision Framework
Ask these questions in order:
- Is it a security-related warning?
Examples includeApexCRUDViolation,ApexSOQLInjection,ApexSharingViolations, and hardcoded credentials. If yes, fix the issue. You should rarely suppress security warnings. - Does fixing it require a large change for a small risk?
For example, imagine an old batch class that processes only five records and works correctly. A major rewrite could introduce new issues in a stable process that developers rarely change. In this case, you can suppress the warning, but clearly explain why. - Is PMD warning about something that is acceptable in your specific situation?
PMD applies general rules and may not understand every situation. For example, you may intentionally leave a catch block empty because a third-party library is expected to fail silently. In such cases, suppressing the warning can be appropriate. - Could suppressing the warning hide a real performance or governor limit problem?
If yes, fix the issue instead of suppressing it. A problem may not appear in a sandbox with only a few test records, but it can fail in production when the system handles a large amount of data. - Can you fix it quickly and easily?
If the fix takes only a few minutes, fix it. For example, you can replace aSystem.debug()statement with a proper logging utility or bulkify a SOQL query. Do not use suppression just because fixing the issue requires a small amount of effort.
If you decide to suppress a warning, always add a comment explaining the reason next to the suppression. This helps the next developer understand why you ignored the warning and prevents important risks from being overlooked.
How to Suppress PMD Warnings in Apex
Apex supports two suppression mechanisms: the @SuppressWarnings annotation and // NOPMD comments. Both work; they differ in scope and visibility.
@SuppressWarnings
@SuppressWarnings is a standard annotation you place directly above the class, method, or field you want to exempt. The rule name goes inside parentheses, prefixed with PMD.:
@SuppressWarnings('PMD.AvoidGlobalModifier')
global class CustomerController {
global void getCustomerById(String custId) {
// implementation
}
}
Method-level suppression
If only one method needs the exemption, put the annotation on the method, not the class:
Class-level suppression
Use class-level suppression only when the issue genuinely applies to the whole class, for example, a legacy class that’s flagged for excessive length and can’t be safely split without a full regression cycle:
@SuppressWarnings('PMD.ExcessiveClassLength')
public class LegacyBillingEngine {
// large, stable, rarely modified business logic
}
// NOPMD
// NOPMD is an inline comment suppression. It suppresses PMD for the specific line it’s attached to, and it’s the right tool when you want to silence one line inside a method without exempting the whole method:
try {
ExternalLibrary.callMethod();
} catch (Exception e) {
// NOPMD: third-party library is expected to fail silently on timeout
}
Always put a reason after// NOPMD with nothing else tells the next reader nothing.
Suppressing multiple rules
Both mechanisms support suppressing more than one rule at once. For @SuppressWarnings, separate rule names with commas inside the same string:
@SuppressWarnings('PMD.AvoidGlobalModifier, PMD.ExcessivePublicCount')
global class LegacyIntegrationController {
// ...
}
For inline comments, list each rule after the colon:
// NOPMD: AvoidSOQLInLoops, AvoidDebugStatements - legacy batch, capped at 5 records
Common Salesforce Apex PMD Warnings and How to Fix Them
In most Salesforce teams, PMD discussions tend to focus on the same set of common warnings. Here’s a look at what these rules check and how you can properly fix the underlying issues instead of simply suppressing the warnings.
ApexCRUDViolation
What it flags: A DML or SOQL operation performed without a preceding CRUD/FLS permission check. Since Apex runs in system mode by default, this can let a user act on records or fields they shouldn’t have access to.
How to fix it: Check Schema.sObjectType.Account.isAccessible(), isCreateable(), isUpdateable(), or isDeletable() before the operation, or use WITH SECURITY_ENFORCED / WITH USER_MODE on your SOQL and Security. stripInaccessible() before DML on user-supplied data.
apex
if (Schema.sObjectType.Account.isUpdateable()) {
update acc;
}
Read Guide: Enforce Object-level and Field-level permissions in Apex
ApexSOQLInjection
What it flags: PMD flags code that directly adds an untrusted variable to a SOQL or SOSL query string.
How to fix it: Use bind variables instead of string concatenation. Salesforce automatically escapes bind variables, which helps prevent SOQL injection.
// Vulnerable
String query = 'SELECT Id FROM Account WHERE Name = \'' + userInput + '\'';
// Safe
List<Account> accs = [SELECT Id FROM Account WHERE Name = :userInput];
Read Guide: What is SOQL Injection?
ApexSharingViolations
What it checks: This rule flags classes that perform DML but do not specify a sharing mode such as with sharing, without sharing, or inherited sharing.
How to fix it: Add an explicit sharing declaration to the class. In most cases, use with sharing to enforce record-level security. Use without sharing only when you have a clear and documented reason to bypass it.
public with sharing class OpportunityService {
// DML here now respects the running user's sharing rules
}
AvoidSOQLInLoops
What it flags: This rule flags SOQL queries that run inside a loop. As the number of records increases, the code can quickly hit Salesforce’s 100 SOQL query limit.
How to fix it: Collect the required record IDs in a Set first, then run one SOQL query outside the loop to retrieve all the records you need. This makes the code bulk-safe and more efficient.
Set<Id> accountIds = new Set<Id>();
for (Contact c : contacts) {
accountIds.add(c.AccountId);
}
Map<Id, Account> accounts = new Map<Id, Account>(
[SELECT Id, Name FROM Account WHERE Id IN :accountIds]
);
Read Guide: Optimizing Salesforce Apex Code
AvoidDMLStatementsInLoops
What it flags: This rule flags insert, update, delete, or upsert statements inside a loop. Running DML inside a loop can quickly reach Salesforce’s limit of 150 DML statements per transaction.
How to fix it: Add the records to a list while the loop runs. After the loop finishes, perform one bulk DML operation on the list.
List<Account> toUpdate = new List<Account>();
for (Account acc : accounts) {
acc.Status__c = 'Reviewed';
toUpdate.add(acc);
}
update toUpdate;
Read Guide: Optimizing Loop in Apex Code
AvoidDebugStatements
What it flags:System.debug() statements left in production code. These statements can use CPU time and add unnecessary data to debug logs. They also do not provide a proper logging solution for production applications.
How to fix it:
Remove System.debug() statements before deployment. If you need logging in production, use a proper logging framework, such as a custom object, a Platform Event, or a tool like Nebula Logger. A good logging solution should let you enable or disable logging without changing every Apex class.
AvoidGlobalModifier
What it flags: This rule flags classes, methods, or variables declared with the global modifier. In a managed package, once you release a global API, you generally cannot safely remove it or change its signature.
How to fix it: Use public unless the code must be exposed as part of a managed package’s external API. Before using global, make sure external subscribers really need access to it.
ExcessiveClassLength
What it flags: This rule flags classes that have become too long and difficult to review or maintain.
How to fix it: Break the class into smaller classes based on responsibility. For example, move business logic into a service class, SOQL queries into a selector class, and record-level logic into a domain class. This warning usually points to a design problem, so refactoring the class is a better solution than making a small code change or suppressing the warning.
AvoidLogicInTrigger
What it flags: This rule flags business logic written directly inside a trigger instead of a handler class.
How to fix it: Keep the trigger simple. It should only call a handler class or method. Move all business logic into a separate class that the trigger calls.
trigger AccountTrigger on Account (before update) {
AccountTriggerHandler.handleBeforeUpdate(Trigger.new, Trigger.oldMap);
}
- 10 PMD Issues Salesforce Developers Should Focus on in Apex
- Top Mistakes Developers Make in Salesforce Apex Triggers
Real-World Examples of PMD Suppression
Here is what a valid PMD suppression looks like in a real project, based on the reason for the suppression.
False Positive
Sometimes PMD flags code that is already correct because it does not understand the full context. For example, you may intentionally use an empty if block as a documented no-op, or use System.debug() through a custom logging utility that PMD does not recognize. In these cases, the warning may be a false positive, so suppression can be appropriate.
@SuppressWarnings('PMD.EmptyStatementBlock')
public void handleOptionalStep(Boolean skipStep) {
if (skipStep) {
// Intentionally empty — this branch documents that skipping is expected,
// not an oversight. Reviewed by team lead 2026-06-10.
} else {
runStep();
}
}
Legacy code
Older classes sometimes carry violations that would take a full regression cycle to fix safely, and the business hasn’t prioritized that work.
@SuppressWarnings('PMD.ExcessiveClassLength')
public class LegacyOrderProcessor {
// 900+ lines, stable in production for 6 years.
// Refactor tracked in JIRA-4821; deferred pending Q3 regression capacity.
}
Third-Party Code
Integrations with external systems may require coding patterns that PMD flags even though they are correct for that API. For example, you may intentionally ignore an error when a service normally times out as part of its expected behavior.
try {
PaymentGateway.charge(request);
} catch (CalloutException e) {
// NOPMD: Gateway sandbox times out intermittently by design; retried by queueable job
}
Business constraint
Sometimes a real business rule limits data volume in a way PMD’s generic pattern can’t see.
// NOPMD: AvoidSOQLInLoops - Business rule caps this list at 5 regional managers, documented in FR-2291
for (String managerName : regionalManagers) {
Account acc = [SELECT Id FROM Account WHERE Owner.Name = :managerName LIMIT 1];
processAccount(acc);
}
Every one of these examples includes a reason and, ideally, a reference to a ticket or a name. That’s what separates a defensible suppression from technical debt hiding in plain sight.
Configuring PMD Rulesets for Salesforce
Instead of suppressing the same false positive in many files, update the PMD ruleset itself. A custom Apex ruleset XML file lets you:
- Disable a rule across the entire project if it does not apply to your codebase.
- Lower a rule’s priority so it does not fail the build but still appears as a lower-severity warning.
- Change rule settings to match your team’s standards. For example, you can increase the
ExcessiveParameterListlimit from 4 to 6 if your team allows up to six parameters.
A minimal custom ruleset looks like this:
Point your CLI or CI job at this ruleset file instead of the full default set. This is almost always a better long-term fix than scattering suppressions through your codebase.
Running PMD in CI/CD
Run PMD checks before you merge code, not after you deploy it. This gives developers time to review and fix violations instead of rushing to unblock a failed pipeline.
Salesforce CLI
If you use Salesforce Extensions for VS Code, you can run PMD locally with the Apex PMD extension.
For CI/CD, most teams run the standalone PMD tool directly against the force-app source directory. You can also provide your custom PMD ruleset to control which rules the pipeline checks.
pmd check -d force-app -R rulesets/apex-ruleset.xml -f text
GitHub Actions
A simple job step runs PMD and fails the build on violations above a chosen priority:
- name: Run PMD
run: |
pmd check -d force-app -R rulesets/apex-ruleset.xml -f text --fail-on-violation true
Jenkins
In your Jenkinsfile, add a stage that runs the same PMD command and saves the report as a build artifact. This lets reviewers check PMD violations without running the scan on their local machines.
stage('PMD Analysis') {
steps {
sh 'pmd check -d force-app -R rulesets/apex-ruleset.xml -f xml -r pmd-report.xml'
archiveArtifacts artifacts: 'pmd-report.xml'
}
}
Pull request quality gates
Wire the PMD step into your PR checks so a failing scan blocks merge, not just deployment. Most teams fail the build only on priority 1–2 violations and let lower-priority findings surface as warnings in the PR, keeping the gate strict on security and correctness without blocking every stylistic nit.
PMD Suppression Best Practices
- Use the smallest possible scope. Suppress the warning on a single line if possible. If that does not work, suppress it at the method level, and then at the class level.
- Always explain why you suppressed it. A suppression without a comment can hide the problem from future developers.
- Add a ticket or reference when needed. If you plan to fix the issue later, add the related ticket number or reference so others can track the decision.
- Review suppressions regularly. Check your codebase every few months for
@SuppressWarnings('PMD')andNOPMD. Remove suppressions that you no longer need. - Do not suppress warnings just to pass a build. If you need a temporary suppression because of a deadline, create a follow-up ticket to address it later. Otherwise, temporary fixes can become permanent technical debt.
- Update the ruleset for repeated false positives. If the same warning appears across many files and does not apply to your project, consider changing the PMD ruleset instead of adding suppressions
PMD Rules You Should Rarely Suppress
You should almost never suppress the following PMD rules:
ApexCRUDViolationApexSOQLInjectionApexSharingViolationsApexSuggestUsingNamedCred(hardcoded credentials)AvoidHardcodingId
These rules focus on security, so take their warnings seriously. Ignoring a real issue can create a security problem in production, not just a code quality issue.
If you think one of these warnings does not apply to your code, ask another developer to review it before you suppress the warning. A second opinion can help confirm that you are not hiding a real security risk.
PMD Suppression Checklist
Before adding a PMD suppression, make sure you have checked the following:
- ✅ I have read the PMD rule documentation and understand what the rule checks.
- ✅ This is not a security rule such as
ApexCRUDViolation,ApexSOQLInjection,ApexSharingViolations, or hardcoded credentials. - ✅ I have considered fixing the issue and have a clear reason for not fixing it now.
- ✅ I am applying the suppression to the smallest possible scope: line → method → class.
- ✅ I have added a comment or annotation explaining why I suppressed the warning. If I plan to fix it later, I have also added the related ticket reference.
- ✅ A teammate or code reviewer has reviewed the suppression instead of relying only on the CI check.
- ✅ I have added the suppression to a list or tracking document so the team can review it regularly.
Frequently Asked Questions
1. Can I ignore PMD warnings in Apex?
Yes, but simply ignoring a warning does not remove it. PMD will report it again every time you run a scan. If a warning does not apply to your code, suppress it explicitly with @SuppressWarnings or // NOPMD and explain why you suppressed it.
2. How do I use NOPMD in Apex?
Add // NOPMD: <reason> on the same line as the code you want PMD to ignore. This suppresses the warning for that line only. You can also specify the rule name, for example: // NOPMD: AvoidSOQLInLoops - capped list, see JIRA-1123.
3. How do I suppress a single PMD rule?
Use @SuppressWarnings('PMD.RuleName') above the class or method, or use // NOPMD: RuleName on the specific line. Replace RuleName with the exact rule name shown in the PMD warning, such as AvoidGlobalModifier or ExcessiveClassLength.
4. Can I suppress PMD warnings at the method level?
Yes. Add @SuppressWarnings('PMD.RuleName') directly above the method. This is usually the best approach because it suppresses the warning only for that method. PMD will continue to check the rest of the class.
5. Should I suppress security-related PMD rules?
Only in rare cases and after careful review. Rules such as ApexCRUDViolation, ApexSOQLInjection, and ApexSharingViolations help find real security problems. Treat any suppression of these rules as an exception and consider having another developer review it.
6. How do I configure PMD in CI/CD?
Add a PMD step to your CI/CD pipeline, such as GitHub Actions or Jenkins. Configure it to use your project’s ruleset and fail the build when violations exceed your chosen priority level. Run PMD as part of pull request checks so developers can fix problems before merging the code.
Summary
Suppressing PMD warnings can be useful when a rule does not apply to your specific situation, but use it carefully. Always fix real issues, especially security-related problems. When you need to suppress a warning, keep the scope as small as possible and document the reason. Regularly review your rules and suppressions to prevent them from becoming a habit that hides potential security, performance, or code-quality issues.
Related Posts
- How to Manage Technical Debt in Salesforce
- Optimizing Salesforce Apex Code
- DML or SOQL Inside Loops
- Limiting data rows for lists
- Disabling Debug Mode for production
- Stop Describing every time and use Caching of the described object
- Use Filter in SOQL
- Avoid Heap Size
- Optimize Trigger
- Bulkify Code
- Foreign Key Relationship in SOQL
- Defer Sharing Rules
- Avoid Hardcode in code
- Use Platform Caching
- Top Mistakes Developers Make in Salesforce Apex Triggers
- Best Code Analysis Tools For Salesforce Development
- What is SOQL Injection?
References
PMD | Engines | Salesforce Code Analyzer
Want to improve your Salesforce code quality?
Not sure whether to fix or suppress a PMD warning? See our Salesforce development best practices. or contact us.

Dhanik Lal Sahni is a Salesforce Solution Architect, Independent Consultant, and the founder of SalesforceCodex.com. With nearly two decades of IT experience and extensive expertise in Salesforce architecture, he helps startups and enterprises design scalable, secure, and high-performance CRM solutions using Sales Cloud, Service Cloud, Experience Cloud, Data Cloud, Apex, Lightning Web Components (LWC), and enterprise integration patterns.
Through SalesforceCodex, he shares practical tutorials, real-world implementation guides, enterprise architecture best practices, and interview preparation resources for Salesforce Developers, Architects, and Administrators.
Learn more about his Salesforce consulting and freelance services at dhaniksahni.com.
