Apex Trigger Best Practices (All-in-One)

In this post, I will explain all the Salesforce Apex Trigger Best Practices that everyone must follow before writing an Apex Trigger.

Apex Code can be executed from the Triggers. And Triggers are executed before or after the Salesforce Records are created, updated, deleted, or undeleted. Hence, it is absolutely necessary to follow the Best Practices while writing an Apex Trigger.

Apex Trigger Best Practices

Follow below Best Practices while writing an Apex Trigger. There are around 12 of them. I have mentioned all of them in one snapshot for you to keep it at one place at the bottom of this post:

1. One Trigger Per Object

We should write only 1 Trigger per Object. This is essential because if there are multiple Triggers on a single Object, we can not guarantee the order of execution for the multiple Triggers.

2. Follow Naming Convention

Decide the appropriate name for your Trigger. As Trigger is related to Object, the name of the Trigger should be Object-specific and should follow the same naming convention for all the Triggers in the Org. For Example, it should be something like AccountTrigger, CaseTrigger for Account, and Case Triggers respectively.

3. Use only Required Events in Trigger

Add only required Events in Trigger. For example, if you want to execute the Trigger after inserting an Account record, only add after insert event like below:

trigger AccountTrigger on Account (after insert) {
    //Trigger Logic.
}

4. Context-specific Handler Methods

We should have different handler methods for each Trigger Context. It improves the code readability. It also helps other developers to edit the Triggers easily. A sample example for the Context-Specific Handler methods is explained in the next Best Practice that is Logic-less Triggers.

5. Logic-less Triggers

Logic-less Triggers means that we should not write any Business logic in the Triggers. All the Business Logic should be written in separate Apex Class which is know as Trigger Handler in this context. This class can have multiple methods to handle the Business Logic. And, we should only call these methods from the Trigger. Below is the sample example to illustrate the Context-specific Handler Methods and Logic-less Triggers.

AccountTrigger.apxt

trigger AccountTrigger on Account (after insert, after update) {
    
    if(Trigger.isBefore){
        // Call Methods for Before Triggers.        
    }
    
    if(Trigger.isAfter){
        
        // Call After Insert methods.
        if(Trigger.isInsert){
            AccountTriggerHandler.afterInsertHandler(Trigger.new);
        }
        
        // Call After Update methods.
        if(Trigger.isUpdate){
            AccountTriggerHandler.afterUpdateHandler(Trigger.new);
        }
    }
}

Consider the above Trigger, we have two separate IF condition for after and before context. Then in each of the contexts, we have separate IF conditions for isInsert, isUpdate context. In each of these context conditions, we are calling a respective static method from the AccountTriggerHandler Apex Class.

AccountTriggerHandler.apxc

public class AccountTriggerHandler{
	
    public static void afterInsertHandler(list<Account> lstAccount){
        // Business logic.
    }
    
    public static void afterUpdateHandler(list<Account> lstAccount){
        // Business logic.
    } 
}

Here, we have created separate methods for each context. Our Trigger is Logic-less as it does not have any Business logic. All the Business logic should be written in Context-specific Handler Methods of AccountTriggerHandler Class.

6. Bulkification

Make sure the Trigger handles multiple records at a time. Always write the Trigger logic considering the fact that multiple records can be inserted, updated, or deleted at a time.

7. Use Collections and Avoid SOQL or DML in FOR loops

Always try not to include SOQL and DML in FOR loops. This is applicable not only in Triggers but also in any Apex Code. Make use of Collections like List, Set, and Maps to store the records or other details in Collection to avoid the SOQL or DML in FOR loops. Use Collections to perform DML operations rather than doing it on an individual record. This helps big time to write efficient code. Also, use FOR loops efficiently to minimize the iterations and make use of break statements in FOR loop wherever applicable.

8. Use SOQL Query FOR Loop

SOQL Query can only return 50,000 records at a time. Querying Large Data Sets might exceed the heap size and hit Governor Limits. Try to optimize the Query by putting WHERE conditions in QUERY with LIMIT if applicable. It is recommended to use SOQL Query FOR Loop in such scenarios. Refer to the example below:

// Avoid this
list<Account> accountList = [SELECT Name, Website FROM Account WHERE Type = 'Customer'];
for(Account acc : accountList){
    // Some Logic.
}
        
// Use THIS.
for(Account acc : [SELECT Name, Website FROM Account WHERE Type = 'Customer']){
    // Some Logic.
} 

SOQL Query FOR Loop can process multiple batches of records with the use of internal calls to query() and queryMore().

9. Proper use of Future method

Use the Future method whenever necessary. The Future method runs asynchronously. Hence, consider the fact that it will execute whenever the resources are available. Ideally, we should use Future methods in Triggers only if we need to make API calls from Trigger.

10. Avoid Hard-coding IDs

Never hard-code the ID of the record in Trigger or in any other Apex Code. Unique IDs are assigned automatically by Salesforce once the record is created. Hence, the value of ID present in one Org will never match with the one in other Org. Hard-coding IDs will create problems after deploying the code to another Org.

11. Avoid Recursive Trigger

When we perform a DML operation on a Record in Trigger which executes the same Trigger again and performs the DML operation again (and so on), then it runs for 16 times and fails due to the Governor Limits. To avoid Recursive Trigger, use two static variables with the default value as false for before and after context. Once the Context-specific handler method is executed for the respective context, set it to true. Before calling the Context-specific handler method, check the value of the static variable, and call the method only if the value is false to avoid the Recursive Trigger.

These are most of the Apex Trigger Best Practices that we need to follow but there are two more which we ignore most of the time.

12. Don’t forget about After Undelete

Most of the time, we only consider insert, update, and delete events. In some of the requirements, we might need to consider after undelete as well to make sure the Business logic is intact. Hence, always consider after undelete while writing a Trigger for a particular requirement.

13. Consider Process Builder and Flows before writing Trigger

Most of the small Triggers can be replaced with Process Builders. Even the complex Triggers can be replaced with the combination of Process Builder and Flow. Hence, before writing a Trigger, see if you can implement the same functionality with Process Builder and Flows.

Apex Trigger Best Practices

Below are most of the Salesforce Apex Trigger Best Practices that we should follow:

Salesforce Apex Trigger Best Practices
Salesforce Apex Trigger Best Practices

If you don’t want to miss new posts, please Subscribe here.

To know more about Apex Triggers, you can check the official Salesforce documentation here.

4 thoughts on “Apex Trigger Best Practices (All-in-One)”

  1. Thanks for sharinf your best practices! It is also nice that you mention to use process/flows. Sometimes because of order execution it can be hectic to use trigger + flows + process builder, so better do it right 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *