Logging and Debugging

System.debug Usage

System.debug should only be used for error logging. All other debug statements must be removed before committing code.

Good Example:

try {
    // Process record
} catch (Exception e) {
    System.debug('Error processing record: ' + e.getMessage());
    // Handle error
}

Bad Example:

System.debug('Processing account: ' + account.Name);  // Remove before commit
System.debug('Account ID: ' + account.Id);  // Remove before commit

Pre-commit Logging Checklist

  • Remove all System.debug statements except those used for error logging
  • Keep error logs concise and actionable for production debugging
  • Prefer proper error handling and response wrappers over ad-hoc debug output

Logging Patterns and Best Practices

  • Log errors with context (method name, key identifiers, etc.)
  • Use consistent log message format
  • Include exception stack traces when helpful
  • Don't log sensitive information

Example:

try {
    processRecord(recordId);
} catch (Exception e) {
    System.debug('Error in processRecord for recordId ' + recordId + ': ' + e.getMessage());
    System.debug('Stack trace: ' + e.getStackTraceString());
    throw e;
}

Code Maintenance

Use this section for long-term maintainability decisions: issue tracking, versioning consistency, and controlled deprecation.

TODO/FIXME Comments

TODO and FIXME comments are acceptable if there is a discussion, issue, or bug created to capture that work.

Format:

// TODO: [Issue #123] Refactor this method to use ACME_AccountSelector
// FIXME: [Bug #456] Handle edge case when recordId is null

Bad Example:

// TODO: Fix this later
// FIXME: This doesn't work

API Version Standardization

Use the latest API version from the project. SFDX handles this automatically when creating new files.

  • Don't manually set API version unless necessary
  • Keep API versions consistent across the project
  • Update API versions as part of regular maintenance

Deprecation Process

Use the following minimum deprecation standard until a fuller lifecycle policy is documented:

  1. Document replacement - State what to use instead and why
  2. Set timeline - Define expected removal version/date
  3. Track consumers - Capture known usages and migration tasks in an issue or work item

Example:

/**
 * @description [DEPRECATED] Use ACME_AccountSelector instead
 * @deprecated This method will be removed in version 2.0. Use ACME_AccountSelector.selectByName() instead.
 * @see ACME_AccountSelector
 */
@Deprecated
public static List<SObject> queryRecords(String objectName) {
    // Deprecated implementation
}

Code Analyzer Compliance

Ruleset Reference

All code must comply with the Code Analyzer ruleset your team maintains in the Salesforce source repository (commonly a PMD ruleset file such as rulesets/salesforce.xml referenced from code-analyzer.yml). Full installation, configuration, and command-line usage are covered in Code Analyzer Compliance.

Key Rules to Be Aware Of

Complexity Rules:

  • CognitiveComplexity - Method: 15, Class: 50
  • NcssCount - Method: 60, Class: 1000
  • AvoidDeeplyNestedIfStmts - Maximum depth: 4
  • ExcessiveParameterList - Maximum: 5 parameters

Security Rules:

  • ApexSharingViolations - Must declare sharing model
  • ApexCRUDViolation - Validate CRUD before SOQL/DML
  • ApexSOQLInjection - Use bind variables, not string concatenation

Performance Rules:

  • OperationWithLimitsInLoop - Avoid DML in loops

Code Style Rules:

  • IfStmtsMustUseBraces - Always use braces
  • ForLoopsMustUseBraces - Always use braces
  • ClassNamingConventions - PascalCase for classes
  • MethodNamingConventions - camelCase for methods

Test Rules:

  • ApexAssertionsShouldIncludeMessage - Include messages in assertions
  • ApexUnitTestClassShouldHaveAsserts - Tests must have assertions
  • ApexUnitTestShouldNotUseSeeAllDataTrue - Avoid seeAllData=true

Common Violations and Solutions

Violation: Cognitive Complexity Too High

Solution: Refactor into smaller methods, use early returns, reduce nesting

Violation: DML in Loop

Solution: Collect records in a list, perform DML once outside the loop

Violation: Missing Sharing Declaration

Solution: Add with sharing, without sharing, or inherited sharing to class declaration

Violation: SOQL Injection

Solution: Use bind variables instead of string concatenation

@SuppressWarnings Usage Guidelines

Do not use @SuppressWarnings without prior approval from a technical lead. Prefer refactoring or a ruleset adjustment discussed with the team. If a technical lead approves a suppression, use it sparingly, for the smallest scope possible, and document why in ApexDoc (and reference the approval when helpful, e.g. ticket or decision link).

/**
 * @description Suppresses PMD warning for this method
 * Suppression reason: This method intentionally uses a switch statement
 * for type checking which PMD flags, but is the most readable approach here.
 * Approved by: Technical Lead <name> on <date> (or link to ticket/ADR).
 */
@SuppressWarnings('PMD.AvoidDeeplyNestedIfStmts')
public static String processType(String type) {
    // Implementation
}

Change History

Version 1.0 - 2026-03-26

  • Added initial version history tracking for this document.

Last Updated: 2026-03-26 Version: 1.0