The Problem: Triggers That Call Themselves

If you’ve ever written a trigger that updates a record, only to find that update firing the same trigger again… and again… you’ve met recursion.

Even experienced developers get caught in this — especially when working across multiple objects or in complex update scenarios.

Why Recursive Triggers Happen

Salesforce executes triggers each time a DML operation occurs, including if that DML is initiated from within a trigger.

trigger AccountTrigger on Account (after update) {
    for (Account acc : Trigger.new) {
        acc.Description = 'Updated internally';
        update acc; // This line causes recursion
    }
}

This will call the same trigger again — causing an infinite loop or hitting governor limits.

How to Prevent Trigger Recursion

1. Use a Static Variable

Create a static Boolean flag in a helper class:

public class TriggerHelper {
    public static Boolean isFirstRun = true;
}

In your trigger:

trigger AccountTrigger on Account (after update) {
    if (TriggerHelper.isFirstRun) {
        TriggerHelper.isFirstRun = false;

        // safe update logic
        List<Account> toUpdate = new List<Account>();
        for (Account acc : Trigger.new) {
            acc.Description = 'Updated internally';
            toUpdate.add(acc);
        }
        update toUpdate;
    }
}

This ensures the logic runs only once per transaction.

2. Use a Trigger Framework

If your org is growing, it’s better to adopt a framework-based pattern.

public class TriggerControl {
    public static Set<Id> processedIds = new Set<Id();
}
trigger ContactTrigger on Contact (before update) {
    for (Contact con : Trigger.new) {
        if (!TriggerControl.processedIds.contains(con.Id)) {
            // Business logic here
            TriggerControl.processedIds.add(con.Id);
        }
    }
}

Use Set<Id> to track which records have been processed. Much better for batch-safe logic.