Recursive triggers are big trouble for Salesforce developer. We always wanted to avoid recursive trigger in Salesforce. But if we have a big org which have multiple triggers with parent-child updates, include Process builder and Workflow field updates. Then we can easily face the recursive trigger error.
So today I will share a few methods using which we can easily avoid recursive trigger. While we can use the methods, we should also follow below best practice.

Best practice for triggers:
- One trigger per object – so we don’t have to think about the execution order. As there is no control over which trigger would be executed first.
- Logic-less Triggers – use Helper classes to handle logic.
- Code coverage 100% with Assert statements to verify functionality is working as expected
- Handle recursion – To avoid the recursion on a trigger, make sure your trigger is getting executed only one time. You may encounter the error: ‘Maximum trigger depth exceeded’, if recursion is not handled well.
While the first three points, we can easily follow. The last one is a bit hard so we will check what are our available options.
1. Use Static Boolean Variable
We can use the Static boolean variable to avoid recursion. Initially set this boolean variable to true and once execution is completed set it to false.
While it is good for less number of records (<200 records). I don’t really like this approach because if we have a large set of records it won’t work.
Sample Code
trigger AccountTrigger on Account (before update) {
AccountTriggerHelper.beforeUpdate(trigger.newMap, trigger.oldMap);
}
TriggerHandler
public class AccountTriggerHelper {
//on before update
public static void beforeUpdate(map<ID, Account> newMap, map<ID, Account> oldMap) {
if(checkRecursive.runOnce) {
System.debug('=== Trigger run ==='+newMap.size() );
checkRecursive.runOnce = false;
}
}
}
Here if we update 200+ records then it will only process the first set of records and skip the others.



2. Use Static Set to Store Record Id
So instead of using the Static boolean variable, we can use Set to Store the Ids. And in execution we can check if we have already processed those records then we can skip them else we will process them.
I have modified that a bit and used a map here. So that we don’t need to make separate variables.
Sample code
public class AccountTriggerHelper {
//on before update
public static void beforeUpdate(map<ID, Account> newMap, map<ID, Account> oldMap) {
List<Account> accList = new List<Account>();
if(!checkRecursive.recordIdMap.containsKey('beforeupdate'))
checkRecursive.recordIdMap.put('beforeupdate', new Set<ID>());
for (Account acc: newMap.values()) {
if(!checkRecursive.recordIdMap.get('beforeupdate').contains(acc.Id)){
accList.add(acc);
checkRecursive.recordIdMap.get('beforeupdate').add(acc.Id);
}
}
System.debug('=== Trigger run === '+accList.size()+' === Total Processed Records === '+checkRecursive.recordIdMap.get('beforeupdate').size());
}
}
As we have triggerType as the key so we can use the same map in before, after updates trigger. Here it will process all records one time.



3. Use Trigger on/off switch
While the above option handles most of the scenario. What if we temporarily want to turn off /on trigger execution. This is the most common use case when we are doing bulk upload and want the trigger to not execute. Or from the Same trigger, we are making update and want to skip the triggers. So here we will enhance our first approach and will make it more flexible.
For example: Initially, we update 100 records and in the trigger, we are again updating the same records and we don’t want our trigger to run again. Then we will simply set a flag and once execution is completed we will remove that flag so that other records can process.
Sample Code
trigger AccountTrigger on Account (before update, after update) {
if(trigger.isBefore && checkRecursive.runOnce)
AccountTriggerHelper.beforeUpdate(trigger.newMap, trigger.oldMap);
if(trigger.isAfter && checkRecursive.runOnce)
AccountTriggerHelper.afterUpdate(trigger.newMap, trigger.oldMap);
}
TriggerHandler
public class AccountTriggerHelper {
//on before update
public static void beforeUpdate(map<ID, Account> newMap, map<ID, Account> oldMap) {
System.debug('=== Before Trigger run ==='+newMap.size() );
}
//on after update
public static void afterUpdate(map<ID, Account> newMap, map<ID, Account> oldMap) {
System.debug('=== After Trigger run ==='+newMap.size() );
checkRecursive.runOnce = false;
update [Select ID from Account WHERE Name = 'Demo Accounts bulk Test'];
checkRecursive.runOnce = true;
}
}
So here in After update, we are again updating the same records. while it’s a perfect scenario for the recursive trigger. Our flag will not run the trigger again as we have put the check. Once the execution is completed we will set it to false so that remaining records can process.



We can also bind this with a custom setting so that admins can control this switch from UI without making changes in code.
Finally, our class where we have declared these variables. We have made it global so all triggers can refer to the same variable. Also, its a good practice that we have one global utility where we define all common variables and methods.
public class checkRecursive {
public static map<String, Set<Id>> recordIdMap = new map<String, Set<Id>>(); //Recommended
public static boolean runOnce = true; //Not recommended
}
Check the demo of Einstein Vision With object Detection and OCR here.
So which approach you like most and follow in your development. Let me know in the comments. Happy Programming 🙂
Thanks for explaining this in details. I tried to use collection SET and ran into slight issue. In Before Insert ID is NULL do you know how we can handle this situation
Recursive trigger will only come during update. You don’t need to worry about insert.