Naming conventions, documentation standards, code structure rules, and PMD compliance for Apex
From claude-sfdx-iqnpx claudepluginhub bhanu91221/claude-sfdx-iq --plugin claude-sfdx-iqThis skill uses the workspace's default tool permissions.
Designs and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Enables AI agents to execute x402 payments with per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents pay for APIs, services, or other agents.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Use PascalCase for all class names. The name should describe the responsibility.
| Type | Convention | Example |
|---|---|---|
| Service | {Object}Service | AccountService |
| Selector | {Object}sSelector | AccountsSelector |
| Domain | {Object}s (plural) | Accounts |
| Controller | {Feature}Controller | InvoiceController |
| Trigger Handler | {Object}TriggerHandler | AccountTriggerHandler |
| Batch | {Action}{Object}Batch | CleanupAccountBatch |
| Schedulable | {Action}{Object}Scheduler | CleanupAccountScheduler |
| Test | {ClassUnderTest}Test | AccountServiceTest |
| Exception | {Name}Exception | ValidationException |
| Interface | I{Name} | INotificationSender |
| Utility/Helper | {Domain}Util or {Domain}Helper | DateUtil |
Use camelCase. Methods should start with a verb.
public void activateAccount(Id accountId) { }
public List<Account> getActiveAccounts() { }
public Boolean isEligibleForDiscount(Opportunity opp) { }
private void validateRequiredFields(Account acc) { }
Naming patterns:
get* -- return dataset* -- assign a valueis* / has* / can* -- return Booleanvalidate* -- throw or addError on failureprocess* / handle* -- perform a complex operationcreate* / build* -- construct and return an objectUse camelCase for all local variables, parameters, and instance variables.
String accountName = 'Acme';
List<Contact> relatedContacts = new List<Contact>();
Map<Id, Account> accountsById = new Map<Id, Account>();
Integer retryCount = 0;
Collection naming:
accounts, lineItems){value}By{Key} (accountsById, contactsByEmail){item}Ids or {item}Set (accountIds, processedRecords)Use UPPER_SNAKE_CASE for all constants. Define them as static final.
private static final Integer MAX_RETRY_COUNT = 3;
private static final String STATUS_ACTIVE = 'Active';
private static final Decimal DEFAULT_TAX_RATE = 0.08;
public static final String ERROR_MISSING_NAME = 'Account name is required.';
No magic numbers or strings. Every literal that has business meaning must be a named constant.
// BAD
if (retryCount > 3) { ... }
if (acc.Status__c == 'Active') { ... }
// GOOD
if (retryCount > MAX_RETRY_COUNT) { ... }
if (acc.Status__c == STATUS_ACTIVE) { ... }
Every public and global method and class must have ApexDoc comments.
/**
* @description Service class for Account-related business operations.
* Handles activation, deactivation, and ownership transfers.
* @author Your Name
* @date 2024-01-15
*/
public with sharing class AccountService {
/**
* @description Activates the given accounts by setting status and date fields.
* Sends notification to account owners on completion.
* @param accountIds Set of Account IDs to activate
* @return List of Database.SaveResult from the update
* @throws AccountServiceException if any account is already active
*/
public static List<Database.SaveResult> activateAccounts(Set<Id> accountIds) {
// implementation
}
}
Required tags:
@description -- always; explain purpose, not implementation@param -- one per parameter, describe expected values@return -- if non-void; describe what is returned and when it may be null@throws -- if the method can throw; describe the conditionOptional tags:
@author -- on class-level doc@date -- on class-level doc@see -- reference to related class or method@example -- usage example for complex APIsOrganize class members in this order:
public with sharing class AccountService {
// 1. Constants
private static final String STATUS_ACTIVE = 'Active';
// 2. Static variables
private static Set<Id> processedIds = new Set<Id>();
// 3. Instance variables
private IAccountSelector selector;
// 4. Constructors
public AccountService() {
this.selector = new AccountsSelector();
}
// 5. Public methods
public void activateAccounts(Set<Id> ids) { }
// 6. Private methods
private void validateAccounts(List<Account> accounts) { }
// 7. Inner classes / interfaces / enums
public class AccountServiceException extends Exception { }
}
// BAD -- deeply nested
for (Account acc : accounts) {
if (acc.Status__c == 'Active') {
for (Contact con : acc.Contacts) {
if (con.Email != null) {
// logic buried three levels deep
}
}
}
}
// GOOD -- extract methods
List<Account> activeAccounts = filterActive(accounts);
List<Contact> contactsWithEmail = getContactsWithEmail(activeAccounts);
processContacts(contactsWithEmail);
The following PMD rules must be satisfied. Configure them in your .pmd/apex-ruleset.xml.
| Rule | Description |
|---|---|
ApexCRUDViolation | Missing CRUD/FLS checks before DML |
ApexSOQLInjection | Dynamic SOQL with unescaped user input |
ApexSharingViolations | Class missing sharing keyword |
AvoidDmlStatementsInLoops | DML inside a loop |
AvoidSoqlInLoops | SOQL inside a loop |
OperationWithLimitsInLoop | Limits-consuming operation in a loop |
| Rule | Description |
|---|---|
CyclomaticComplexity | Method complexity above threshold |
ExcessiveParameterList | More than 4 parameters |
ExcessiveClassLength | Class exceeds 500 lines |
NcssMethodCount | Method exceeds 50 statements |
AvoidGlobalModifier | Use public unless building a managed package |
EmptyCatchBlock | Catch block with no handling |
| Rule | Description |
|---|---|
ApexDoc | Missing documentation on public methods |
FieldNamingConventions | Variable naming does not match convention |
OneDeclarationPerLine | Multiple declarations on same line |
IfStmtsMustUseBraces | If/else without braces |
Every class must declare a sharing keyword. No exceptions.
public with sharing class MyService { } // Default for most classes
public without sharing class SystemService { } // Justified elevated access
public inherited sharing class MySelector { } // Inherit caller's context
public for API methodsprivate for internal methods@TestVisible private for methods that need test access but are not APIglobal unless building a managed package or @RestResourceAlways handle null inputs defensively.
public static void processAccounts(List<Account> accounts) {
if (accounts == null || accounts.isEmpty()) {
return;
}
// proceed
}
Use String.isBlank() and String.isNotBlank() instead of null and empty checks.
// BAD
if (name != null && name != '') { }
// GOOD
if (String.isNotBlank(name)) { }
Use ternary for simple assignments only. Complex logic belongs in if/else.
// OK
String label = isActive ? 'Active' : 'Inactive';
// NOT OK -- too complex for ternary
String msg = (acc.Type == 'Partner' && acc.Status__c == 'Active')
? 'Active Partner'
: (acc.Type == 'Customer' ? 'Customer' : 'Other');
Before submitting code for review, verify: