back to blog
Apex Best Practices
September 29 2023 • 15 min read

For Apex Classes and test classes

  • Class name should always be in pascalcase i.e. SampleApexClass

    public with sharing class ApexClass {
       
    }
  • Method names and variable names should always be in camelcase i.e. sampleVariable, sampleMethod etc…

screenshot 2023 08 31 144233

  • It is not recommended to use snake case i.e. sample_variable
  • Follow the Single Responsibility Principle (SRP), which states that a class should have only one reason to change. Each class should have a clear and well-defined purpose.
  • Aim for high cohesion within your classes, meaning that methods within a class should be related and work together to achieve a single purpose.
  • Explicitly specify access modifiers (global, public, private, or protected) for class members to control their visibility and accessibility.
  • Avoid using DML statements or SOQL queries inside for loops.
//Never use SOQL or DML statements inside for loops
for(Opportunity opp : opportunities) {
   Account acc = [Select Id, Name from Account where Id =: opp.AccountId];
}
//Instead collect all the account Ids and perform a bulk query
List<Id> accountIds = new List<Id>();
for(Opportunity opp : opportunities) {
    accountIds.add(opp.AccountId);
}
List<Account> accounts = [Select Id, Name from Account where Id =: accountIds];
  • A map can also be used to store the queried data if you have to perform some logic based on the Id.
List<Id> userIds = new List<Id>();
for(Employee__c employee : employees) {
    userIds.add(employee.User__c);
}
Map<Id, Employee__c> employeeMap = new Map<Id, Employee__c>([Select Id, User__c from Employee__c WHERE User__c = : userIds]);
  • If an apex class is created for the purpose of being used in an Aura component or LWC, the name of the class should end with Controller.
public with sharing class TimesheetLineItemController{
   
}
  • When performing SOQL queries, pull only  the fields that you will be working on, so that the queries run faster.
  • Do not hardcode recordId or recordType Id or any object Ids in apex.
  • Use Try and catch blocks to handle errors efficiently and catch unexpected errors during DML or SOQL operations.
  • If you are working on an application to publish in AppExchange, use CRUD and FLS check before performing SOQL or DML operations.
  • Make sure to not duplicate the code. Whenever a logic or a set of code is being repeated, create a separate method for that piece of code so that if you want to make changes in the future you will be making changes once. This makes the code more modular and reusable.
  • Break each and every functionality in the code into individual methods as much as possible.
  • Ensure that your test classes provide sufficient code coverage for the production code. Your tests should cover at least 75% of your Apex code, and all triggers must have some coverage as well.
  • Name your test classes and methods descriptively so that it's clear what they are testing. Following a naming convention like ClassNameTest or ClassNameTestMethod can be helpful
  • Create test data within your test classes rather than relying on existing data in your Salesforce org. Use the @TestSetup method to create common test data that multiple test methods can use.
  • Test for expected exceptions using try-catch blocks. Ensure that your code behaves correctly when exceptions are thrown.
  • Make sure to declare the sharing model when creating apex classes by using with sharing, without sharing keywords to set the record access based on the requirement.
public with sharing class MyClass {
    // Code that respects the organization's sharing settings
}
public without sharing class MyAdminClass {
    // Code that bypasses the organization's sharing settings
}
  • SOQL queries can be used in the for condition of a loop
for (SObjectVar : [SELECT Field1, Field2 FROM Object WHERE condition]) {
    // Code to process each record (SObjectVar)
}
  • Avoid nested loops by abstracting the logic into different methods
  • Avoid returning JSON to LWC, instead send and receive them as objects letting the Salesforce platform handle the data transfer.

 For Apex Triggers

  • Stick to the "one trigger per object, per event" pattern. This means having one trigger for before insert, one for before update, etc. This helps keep trigger logic organized and easier to maintain.
  • When working with Triggers do not write the logic in the trigger, instead create a triggerHandler to perform the logic
trigger EmployeeTrigger on Employee__c (before insert, before update) {
   
    if(Trigger.isBefore && Trigger.isInsert) {
       
        EmployeeTriggerHandler.checkUserDuplicationForEmployeeBeforeInsert(Trigger.New);
       
    } else if (Trigger.isBefore && Trigger.isUpdate) {
       
        EmployeeTriggerHandler.checkUserDuplicationForEmployeeBeforeUpdate(Trigger.New);
  • Bulkify apex by passing a list of records in triggers instead of passing a single record because more than one record may enter the trigger at a time.
public with sharing class EmployeeTriggerHandler {
   
   public static void checkUserDuplicationForEmployeeBeforeInsert(List<Employee__c  > employees){
  • Whenever possible, use "before" triggers (before insert, before update, etc.) to modify record data. After triggers should mainly be used for actions that depend on the final state of records.
  • Be cautious of trigger recursion, where triggers update records that trigger other triggers. Use static variables or other mechanisms to prevent infinite loops.
public class TriggerHandler {
    // Static variable to prevent trigger recursion
    private static boolean isExecuting = false;

    public static void handleBeforeInsert(List<MyObject__c> newList) {
        if (!isExecuting) {
            isExecuting = true;
            // Your trigger logic here for BEFORE INSERT
            isExecuting = false;
        }
    }

    public static void handleAfterInsert(List<MyObject__c> newList, Map<Id, MyObject__c> newMap) {
        if (!isExecuting) {
            isExecuting = true;
            // Your trigger logic here for AFTER INSERT
            isExecuting = false;
        }
    }
}

Leave a Comment

Your email address will not be published

© 2024 Digital Biz Tech