Best Practices to Prevent Recursion in Apex Triggers

Introduction:

Recursion in Apex triggers can lead to unexpected behavior and, in some cases, errors like ‘Maximum trigger depth exceeded.’ In this blog, we’ll explore common methods to prevent recursion in Apex triggers and discuss best practices for effective trigger development.

What is a Recursive Trigger?

A recursive trigger occurs when a trigger executes an action, such as an update or insert, and then triggers itself by performing a similar action. This can lead to multiple trigger executions and undesirable outcomes.

Methods to Avoid Recursion in Trigger:
1. Use Static Boolean Variable:

One approach to preventing recursion is to use a static Boolean variable in a helper class. This variable acts as a flag to control whether the trigger logic should be executed. Here’s an example:

// Apex Class with Static Variable
public class RecursionHandler {
    public static Boolean runOnce = true;
}
// Apex Trigger example for handling recursion
trigger AccountTrigger on Account(after update){
    List<Account> accountListToUpdate = new List<Account>();
    
    if(RecursionHandler.runOnce){
        RecursionHandler.runOnce = false;
        
        for(Account accountObj : Trigger.New){
            accountObj.Name = 'Updated Name';
            accountListToUpdate.add(accountObj);
        }
    }
    
    if(!accountListToUpdate.isEmpty()){
        update accountListToUpdate;
    }
}
Drawback:

This method has limitations when dealing with a large number of records, as it may process only the first 200 records and skip the rest.

2. Use Old Map:

An alternative method is to compare the new values with the old values using the Trigger.oldMap" before executing the trigger logic. This ensures that the trigger logic is executed only when specific fields have changed:

// Trigger OpportunityTrigger on Opportunity(after update)
for(Opportunity opp : Trigger.New){
    if(opp.Stage != Trigger.oldMap.get(opp.Id).Stage){
        // Add your logic here 
    }
}
Advantage:

This approach ensures that the trigger logic is executed only when relevant changes occur, preventing unnecessary recursion.

3. Limit Recursive Depth:

Add an additional layer of control by limiting the recursive depth. Create a static variable to track the recursion depth and set a threshold. If the recursion depth exceeds the threshold, halt further execution.

// Apex Class with Recursive Depth Limit
public class RecursionHandler {
    public static Integer recursionDepth = 0;
    public static Integer maxDepth = 3; // Set your desired maximum depth
}
// Apex Trigger example with Recursive Depth Limit
trigger CustomObjectTrigger on Custom_Object__c (before update) {
    if(RecursionHandler.recursionDepth < RecursionHandler.maxDepth) {
        RecursionHandler.recursionDepth++;
        
        // Add your trigger logic here
        
        RecursionHandler.recursionDepth--;
    }
}
4. Use Static Set of IDs:

Another effective method is to use a static set of IDs to track processed records. This approach offers more granular control and is particularly beneficial for scenarios involving larger datasets.

// Apex Class with Static Set of IDs
public class TriggerHandler {
    private static Set<Id> processedRecords = new Set<Id>();

    public static void onBeforeUpdate(List<Account> newAccounts) {
        for (Account acc : newAccounts) {
            if (!processedRecords.contains(acc.Id)) {
                // Your logic here
                acc.Name = 'Updated Name';
                
                // Continue with your processing
                
                // Add the record ID to the set to track processed records
                processedRecords.add(acc.Id);
            }
        }
    }
}
Advantage:

This approach efficiently tracks processed records, offering an alternative to the static boolean variable.

Best Practices for Triggers:
One Trigger per Object:

To avoid execution order complexities, have only one trigger per object.

Logic-less Triggers:

Use helper classes to encapsulate logic and maintain clean, modular triggers.

Code Coverage 100%:

Ensure thorough test coverage for your triggers to catch potential issues early.

Handle Recursion:

To prevent recursion, incorporate one of the methods mentioned above. Use a static Boolean variable or compare old values using Trigger.oldMap to control trigger execution.

Limit Recursive Depth:

Introduce a recursive depth limit to further safeguard against unintended recursion.

Closing Remarks:

Ensuring the prevention of recursion in Apex triggers is paramount for upholding data integrity and avoiding trigger-related errors. Through the utilization of static Boolean variables, comparison of old values, and imposition of recursive depth limits, developers can aptly manage trigger execution, thereby bolstering the reliability of their Salesforce applications.