What is Recursion in Apex Triggers?
Recursion occurs when a trigger ends up calling itself multiple times within the same transaction, either directly (by updating the same object) or indirectly (by updating related objects that cause the original trigger to fire again).
This behavior can lead to serious issues, such as:
Why Does This Happen?
Apex triggers are designed to respond to DML events like insert, update, and delete. But if a trigger updates a record that causes another trigger to fire, you can end up in a recursive loop.
This happens more often than you’d expect — especially in:
Let us discuss how we can achieve handling recursion through these 4 things:
Now, let’s dive into a real-world use case involving standard Salesforce objects — Case and Contact. This will help us understand how each technique can be practically applied to avoid trigger recursion and maintain data consistency.
Real-World Use Case: Case and Contact – A Two-Way Trigger Scenario
Let’s imagine a scenario many support-driven businesses face:
Whenever a Case is marked as High Priority, we want to update the related Contact to reflect that urgency — for example, by adding a warning note in their Description field.
Now here’s the twist:
There’s another automation in place where, if a Contact’s Description is changed, all open Cases related to that Contact get updated to show they’ve been “Notified.”
1. Using Static Boolean Variables
This is the most straightforward approach.
We create a static Boolean variable, usually in a helper class, and initialize it as true. When the trigger runs:
- It checks the flag — if it’s true, the logic proceeds.
- After executing it once, it sets the flag to false, ensuring that the logic doesn’t run again during the same transaction.
Apex Trigger:

Apex :

2. Using Static Set
This method is ideal for bulk record processing.
We maintain a static Set<Id> to track processed records.
Before performing any logic or DML, we check whether the record’s ID is already in the set.
How it works:
- If the ID is not in the set, execute the logic and add the ID to the set.
- If it’s already present, skip it — recursion is avoided.

3. Using a Custom Checkbox Field
In some cases, a static variable or trigger context might not be enough — especially when dealing with flows, asynchronous operations, or external integrations. In such situations, we need a persistent way to track whether a record has already been processed.
This is where a custom checkbox field becomes a powerful tool
💡 Why Use a Custom Field?
Instead of relying on in-memory flags (which reset after each transaction), we store a status inside the record itself — for example, High_Priority_Notified__c.

💡 Note: Unlike the other examples which show recursion control from the Case trigger, this one demonstrates how to handle updates from the Contact side using
Trigger.oldMap. The use case still fits within the same scenario — controlling recursion between Case and Contact.
4. Using oldMap to Compare Field Values
This is a clean, memory-efficient way to avoid unnecessary recursion.
In before update or after update triggers, Salesforce provides Trigger.oldMap — which holds the original versions of the records before the update.
You can compare fields from Trigger.oldmap and Trigger.new to see if a specific change actually occurred. If not, you skip your logic.
