Rules that flag suboptimal code.
Edit me

AvoidDebugStatements

Since: PMD 6.36.0

Priority: Medium (3)

Debug statements contribute to longer transactions and consume Apex CPU time even when debug logs are not being captured.

When possible make use of other debugging techniques such as the Apex Replay Debugger and Checkpoints that could cover most use cases.

For other valid use cases that the statement is in fact valid make use of the @SuppressWarnings annotation or the //NOPMD comment.

This rule is defined by the following XPath expression:

//MethodCallExpression[lower-case(@FullMethodName)='system.debug']

Example(s):

public class Foo {
    public void bar() {
        Account acc = [SELECT Name, Owner.Name FROM Account LIMIT 1];
        System.debug(accs); // will get reported
    }

    @SuppressWarnings('PMD.AvoidDebugStatements')
    public void baz() {
        try {
            Account myAccount = bar();
        } catch (Exception e) {
            System.debug(LoggingLevel.ERROR, e.getMessage()); // good to go
        }
    }
}

This rule has the following properties:

Name Default Value Description Multivalued
cc_categories Style Deprecated Code Climate Categories yes. Delimiter is ‘|’.
cc_remediation_points_multiplier 1 Deprecated Code Climate Remediation Points multiplier no
cc_block_highlighting false Deprecated Code Climate Block Highlighting no

Use this rule with the default properties by just referencing it:

<rule ref="category/apex/performance.xml/AvoidDebugStatements" />

AvoidDmlStatementsInLoops

Deprecated

Since: PMD 5.5.0

Priority: Medium (3)

Avoid DML statements inside loops to avoid hitting the DML governor limit. Instead, try to batch up the data into a list and invoke your DML once on that list of data outside the loop.

This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced by the more general rule OperationWithLimitsInLoop.

This rule is defined by the following Java class: net.sourceforge.pmd.lang.apex.rule.performance.AvoidDmlStatementsInLoopsRule

Example(s):

public class Something {
    public void foo() {
        for (Integer i = 0; i < 151; i++) {
            Account account;
            // ...
            insert account;
        }
    }
}

This rule has the following properties:

Name Default Value Description Multivalued
cc_categories Performance Deprecated Code Climate Categories yes. Delimiter is ‘|’.
cc_remediation_points_multiplier 150 Deprecated Code Climate Remediation Points multiplier no
cc_block_highlighting false Deprecated Code Climate Block Highlighting no

Use this rule with the default properties by just referencing it:

<rule ref="category/apex/performance.xml/AvoidDmlStatementsInLoops" />

AvoidSoqlInLoops

Deprecated

Since: PMD 5.5.0

Priority: Medium (3)

New objects created within loops should be checked to see if they can created outside them and reused.

This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced by the more general rule OperationWithLimitsInLoop.

This rule is defined by the following Java class: net.sourceforge.pmd.lang.apex.rule.performance.AvoidSoqlInLoopsRule

Example(s):

public class Something {
    public static void main( String as[] ) {
        for (Integer i = 0; i < 10; i++) {
            List<Account> accounts = [SELECT Id FROM Account];
        }
    }
}

This rule has the following properties:

Name Default Value Description Multivalued
cc_categories Performance Deprecated Code Climate Categories yes. Delimiter is ‘|’.
cc_remediation_points_multiplier 150 Deprecated Code Climate Remediation Points multiplier no
cc_block_highlighting false Deprecated Code Climate Block Highlighting no

Use this rule with the default properties by just referencing it:

<rule ref="category/apex/performance.xml/AvoidSoqlInLoops" />

AvoidSoslInLoops

Deprecated

Since: PMD 6.0.0

Priority: Medium (3)

Sosl calls within loops can cause governor limit exceptions.

This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced by the more general rule OperationWithLimitsInLoop.

This rule is defined by the following Java class: net.sourceforge.pmd.lang.apex.rule.performance.AvoidSoslInLoopsRule

Example(s):

public class Something {
    public static void main( String as[] ) {
        for (Integer i = 0; i < 10; i++) {
            List<List<SObject>> searchList = [FIND 'map*' IN ALL FIELDS RETURNING Account (Id, Name), Contact, Opportunity, Lead];
        }
    }
}

This rule has the following properties:

Name Default Value Description Multivalued
cc_categories Performance Deprecated Code Climate Categories yes. Delimiter is ‘|’.
cc_remediation_points_multiplier 150 Deprecated Code Climate Remediation Points multiplier no
cc_block_highlighting false Deprecated Code Climate Block Highlighting no

Use this rule with the default properties by just referencing it:

<rule ref="category/apex/performance.xml/AvoidSoslInLoops" />

EagerlyLoadedDescribeSObjectResult

Since: PMD 6.40.0

Priority: Medium (3)

This rule finds DescribeSObjectResults which could have been loaded eagerly via SObjectType.getDescribe().

When using SObjectType.getDescribe() or Schema.describeSObjects() without supplying a SObjectDescribeOptions, implicitely it will be using SObjectDescribeOptions.DEFAULT then all child relationships will be loaded eagerly regardless whether this information is needed or not. This has a potential negative performance impact. Instead SObjectType.getDescribe(options) or Schema.describeSObjects(SObjectTypes, options) should be used and a SObjectDescribeOptions should be supplied. By using SObjectDescribeOptions.DEFERRED the describe attributes will be lazily initialized at first use.

Lazy loading DescribeSObjectResult on picklist fields is not recommended. The lazy loaded describe objects might not be 100% accurate. It might be safer to explicitly use SObjectDescribeOptions.FULL in such a case. The same applies when you need the same DescribeSObjectResult to be consistent accross different contexts and API versions.

Properties:

  • noDefault: The behavior of SObjectDescribeOptions.DEFAULT changes from API Version 43 to 44: With API Version 43, the attributes are loaded eagerly. With API Version 44, they are loaded lazily. Simply using SObjectDescribeOptions.DEFAULT doesn’t automatically make use of lazy loading. (unless “Use Improved Schema Caching” critical update is applied, SObjectDescribeOptions.DEFAULT do fallback to lazy loading) With this property enabled, such usages are found. You might ignore this, if you can make sure, that you don’t run a mix of API Versions.

This rule is defined by the following XPath expression:

//MethodCallExpression[(lower-case(@MethodName) = "getdescribe" or lower-case(@MethodName) = "describesobjects") and not(VariableExpression/ReferenceExpression[lower-case(@Image) = "sobjectdescribeoptions" ])] |
//ReferenceExpression[$noDefault = true() and lower-case(@Image) = "sobjectdescribeoptions" and parent::VariableExpression[lower-case(@Image) = "default"]]

Example(s):

public class Foo {
    public static void bar(List<Account> accounts) {
        if (Account.SObjectType.getDescribe(SObjectDescribeOptions.DEFERRED).isCreateable()) {
            insert accounts;
        }
    }
}

This rule has the following properties:

Name Default Value Description Multivalued
cc_categories Style Deprecated Code Climate Categories yes. Delimiter is ‘|’.
cc_remediation_points_multiplier 1 Deprecated Code Climate Remediation Points multiplier no
cc_block_highlighting false Deprecated Code Climate Block Highlighting no
noDefault false Do not allow SObjectDescribeOptions.DEFAULT option to ensure consistent results no matter where getDescribe is called no

Use this rule with the default properties by just referencing it:

<rule ref="category/apex/performance.xml/EagerlyLoadedDescribeSObjectResult" />

Use this rule and customize it:

<rule ref="category/apex/performance.xml/EagerlyLoadedDescribeSObjectResult">
    <properties>
        <property name="noDefault" value="false" />
    </properties>
</rule>

OperationWithLimitsInLoop

Since: PMD 6.29.0

Priority: Medium (3)

Database class methods, DML operations, SOQL queries, SOSL queries, Approval class methods, Email sending, async scheduling or queueing within loops can cause governor limit exceptions. Instead, try to batch up the data into a list and invoke the operation once on that list of data outside the loop.

This rule is defined by the following Java class: net.sourceforge.pmd.lang.apex.rule.performance.OperationWithLimitsInLoopRule

Example(s):

public class Something {
    public void databaseMethodInsideOfLoop(List<Account> accounts) {
        for (Account a : accounts) {
            Database.insert(a);
        }
    }

    public void dmlInsideOfLoop() {
        for (Integer i = 0; i < 151; i++) {
            Account account;
            // ...
            insert account;
        }
    }

    public void soqlInsideOfLoop() {
        for (Integer i = 0; i < 10; i++) {
            List<Account> accounts = [SELECT Id FROM Account];
        }
    }

    public void soslInsideOfLoop() {
        for (Integer i = 0; i < 10; i++) {
            List<List<SObject>> searchList = [FIND 'map*' IN ALL FIELDS RETURNING Account (Id, Name), Contact, Opportunity, Lead];
        }
    }

    public void messageInsideOfLoop() {
        for (Integer i = 0; i < 10; i++) {
            Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
            Messaging.sendEmail(new Messaging.SingleEmailMessage[]{email});
        }
    }

    public void approvalInsideOfLoop(Account[] accs) {
        for (Integer i = 0; i < 10; i++) {
            Account acc = accs[i];
            Approval.ProcessSubmitRequest req = new Approval.ProcessSubmitRequest();
            req.setObjectId(acc.Id);
            Approval.process(req);
            Approval.lock(acc);
            Approval.unlock(acc);
        }
    }

    public void asyncInsideOfLoop() {
        for (Integer i = 0; i < 10; i++) {
            System.enqueueJob(new MyQueueable());
            System.schedule('x', '0 0 0 1 1 ?', new MySchedule());
            System.scheduleBatch(new MyBatch(), 'x', 1);
        }
    }
}

This rule has the following properties:

Name Default Value Description Multivalued
cc_categories Performance Deprecated Code Climate Categories yes. Delimiter is ‘|’.
cc_remediation_points_multiplier 150 Deprecated Code Climate Remediation Points multiplier no
cc_block_highlighting false Deprecated Code Climate Block Highlighting no

Use this rule with the default properties by just referencing it:

<rule ref="category/apex/performance.xml/OperationWithLimitsInLoop" />