By using this site, you agree to the Privacy Policy and Terms of Use.
Accept
horizontal-light horizontal-dark
  • Home
  • Tutorials
    • Salesforce AI
    • Salesforce DevOps
    • Career
    • Errors
    • Interview Questions
    • Salesforce Integration
    • Salesforce Flow
  • Salesforce Tools
  • Apex Development
  • Lightning Web Components
  • Salesforce Admin
  • About
    • Privacy Policy
    • Disclaimer
    • Terms & Conditions
    • Contact
SalesforceCornerSalesforceCorner
Search
  • Home
  • Tutorials
    • Salesforce AI
    • Salesforce DevOps
    • Career
    • Errors
    • Interview Questions
    • Salesforce Integration
    • Salesforce Flow
  • Salesforce Tools
  • Apex Development
  • Lightning Web Components
  • Salesforce Admin
  • About
    • Privacy Policy
    • Disclaimer
    • Terms & Conditions
    • Contact
Follow US
Salesforce Corner » Apex Development » How to Prevent Recursive Triggers in Salesforce Apex
Apex Development

How to Prevent Recursive Triggers in Salesforce Apex

Learn Proven Techniques to Prevent Recursive Triggers and Build Reliable Apex Automation

Neha Panwar
By
Neha Panwar
ByNeha Panwar
Salesforce Developer and Technical Writer
Neha Panwar is a Salesforce developer and technical writer who shares practical tutorials, Apex guides, and real-world solutions for developers. She focuses on simplifying Salesforce concepts,...
Follow:
- Salesforce Developer and Technical Writer
Last updated: 2026/06/28
Share
How to prevent recursive triggers
SHARE

Writing an Apex trigger is straightforward, but writing one that avoids recursion is much more challenging. Recursive triggers are one of the most common issues Salesforce developers face because they can cause infinite loops, governor limit exceptions, and unexpected data updates.

Understanding how to prevent recursive triggers in Salesforce Apex is essential for every developer. Whether you’re updating records in an after trigger, calling helper classes, or combining Apex with Flow automation, recursion can quickly become a serious problem if it isn’t handled correctly.

In this guide, you’ll learn what recursive triggers are, why they happen, different techniques to prevent them, and which approaches Salesforce developers recommend today.

Contents
What Is a Recursive Trigger in Salesforce?Why Do Recursive Triggers Happen?Common Causes of Trigger RecursionHow Salesforce Handles Trigger ExecutionWhy Trigger.oldMap Is BetterMethod 2: Static Boolean PatternLimitations of Static BooleanMethod 3: Use a Static Set (Recommended)Why Static Set Is BetterMethod 4: Use a Static MapMethod 5: Use a Trigger Handler PatternTrigger Frameworks Reduce RecursionCommon Mistakes That Cause Recursive TriggersUpdating Records Inside After UpdateIgnoring Trigger.oldMapUsing Multiple TriggersMixing Apex and Flow Without PlanningBest Practices for Preventing Recursive TriggersTesting Recursive Trigger PreventionStatic Boolean vs Static Set vs Trigger.oldMapFrequently Asked QuestionsWhat is a recursive trigger in Salesforce?Why are recursive triggers dangerous?Is a static Boolean still recommended?What is the best way to prevent recursive triggers?Can Salesforce Flow also cause recursion?Should I update records inside an after update trigger?Final ThoughtsRelated Articles

What Is a Recursive Trigger in Salesforce?

A recursive trigger occurs when a trigger performs a DML operation that causes the same trigger to execute again during the same transaction.

For example:

  1. An Account trigger fires.
  2. The trigger updates the same Account.
  3. The update fires the trigger again.
  4. The process repeats continuously until Salesforce stops it.

Eventually, Salesforce throws an error such as:

Maximum trigger depth exceeded

Besides causing exceptions, recursive triggers also waste governor limits by executing unnecessary SOQL queries and DML operations.

If you’re new to trigger development, read Apex Trigger Tutorial for Beginners in Salesforce before implementing recursion prevention techniques.

Why Do Recursive Triggers Happen?

Most recursion problems occur because developers unintentionally update records inside triggers.

Consider this example:

trigger AccountTrigger on Account (after update){

    List<Account> accountsToUpdate = new List<Account>();

    for(Account acc : Trigger.new){

        acc.Description = 'Updated';

        accountsToUpdate.add(acc);

    }

    update accountsToUpdate;

}

What happens?

  • Trigger executes.
  • Records get updated.
  • Update statement fires the trigger again.
  • Same code executes repeatedly.
  • Salesforce eventually stops the transaction.

Although this example is simple, recursion often becomes harder to identify when helper classes, Flow automation, Process Builder, or managed packages are involved.

Common Causes of Trigger Recursion

Recursive triggers are usually introduced by one of these scenarios:

  • Updating the same object inside an after trigger
  • Multiple triggers on the same object
  • Apex calling Flow
  • Flow updating the same record
  • Process Builder performing updates
  • Cross-object automation
  • Trigger handler methods updating parent records
  • Queueable or Future methods updating the original object

Because modern Salesforce orgs often contain multiple automation tools, recursion may happen even when your Apex code looks correct.

How Salesforce Handles Trigger Execution

Every DML operation starts a new trigger execution.

For example:

Insert Account

↓

Before Insert Trigger

↓

After Insert Trigger

↓

Update Account

↓

Before Update Trigger

↓

After Update Trigger

If one update causes another update, Salesforce keeps repeating the process until one of these happens:

  • Maximum Trigger Depth is reached
  • Governor limits are exceeded
  • Transaction fails

Therefore, recursion prevention should be part of every Apex trigger design.

Dev workspace with Salesforce and VS Code
Dev workspace with Salesforce and VS Code

Method 1: Use Trigger.oldMap (Recommended)

This is usually the safest and simplest solution.

Instead of updating records every time the trigger runs, compare the previous value with the new value.

Example:

trigger OpportunityTrigger on Opportunity(before update){

    for(Opportunity opp : Trigger.new){

        Opportunity oldOpp = Trigger.oldMap.get(opp.Id);

        if(opp.StageName != oldOpp.StageName){

            System.debug('Stage Changed');

        }

    }

}

Since the logic executes only when the field actually changes, unnecessary updates are avoided.

This approach is lightweight, easy to understand, and doesn’t require static variables.

In many projects, Trigger.oldMap alone prevents recursion completely.

Why Trigger.oldMap Is Better

Developers sometimes jump directly to static variables.

However, checking whether data has actually changed is usually a better solution because:

  • No additional memory is required.
  • Logic remains easy to understand.
  • Bulk operations continue to work correctly.
  • False recursion detection is avoided.

Whenever possible, compare old and new values before performing DML.

Method 2: Static Boolean Pattern

For many years, developers used a static Boolean variable.

Example helper class:

public class TriggerHelper{

    public static Boolean firstRun = true;

}

Trigger:

trigger ContactTrigger on Contact(after update){

    if(!TriggerHelper.firstRun){

        return;

    }

    TriggerHelper.firstRun = false;

    // Business Logic

}

Although this approach appears simple, Salesforce architects no longer recommend it for bulk processing.

Why?

Imagine updating 400 records.

Salesforce processes records in batches of 200.

The first batch sets the Boolean to false.

The second batch skips execution completely, leaving records unprocessed. This limitation is one reason the static Boolean approach is now considered an anti-pattern for recursion handling.

For modern Salesforce applications, better alternatives are available.

Limitations of Static Boolean

Static Boolean works only for very simple scenarios.

Problems include:

  • Doesn’t support bulk processing properly.
  • Can skip valid records.
  • Doesn’t distinguish between trigger events.
  • Difficult to maintain in enterprise projects.

Because of these issues, experienced developers rarely use this approach anymore.

Method 3: Use a Static Set (Recommended)

Instead of using a Boolean variable, most Salesforce developers now prefer using a static Set that stores processed record IDs.

Unlike a Boolean, a Set allows every record to be checked individually, making it much more reliable for bulk operations.

First, create a helper class:

public class TriggerHelper{

    public static Set<Id> processedRecords = new Set<Id>();

}

Now update the trigger:

trigger ContactTrigger on Contact(after update){

    List<Contact> contactsToUpdate = new List<Contact>();

    for(Contact con : Trigger.new){

        if(!TriggerHelper.processedRecords.contains(con.Id)){

            TriggerHelper.processedRecords.add(con.Id);

            con.Description = 'Processed';

            contactsToUpdate.add(con);

        }

    }

    if(!contactsToUpdate.isEmpty()){

        update contactsToUpdate;

    }

}

Here, every Contact ID is stored after it has been processed.

If Salesforce fires the trigger again during the same transaction, the trigger ignores records that already exist in the Set.

Compared to the static Boolean approach, this technique works much better for bulk processing and is widely recommended by experienced Salesforce developers.

Why Static Set Is Better

A static Set solves several problems that a Boolean cannot.

Benefits include:

  • Supports bulk processing
  • Tracks each record separately
  • Prevents duplicate execution
  • Easy to implement
  • Suitable for most trigger frameworks

However, even a static Set has limitations.

For example, if the same record is intentionally updated later in the same transaction, the Set may prevent valid business logic from running.

Because of that, Salesforce architects often combine Static Sets with field comparisons and trigger frameworks.

Method 4: Use a Static Map

Large Salesforce projects often have multiple trigger events.

For example:

  • Before Insert
  • Before Update
  • After Insert
  • After Update

Using only one Set makes it difficult to separate these events.

Instead, developers can use a Static Map.

Example helper class:

public class TriggerHelper{

    public static Map<String,Set<Id>> processedMap =
        new Map<String,Set<Id>>();

}

Inside the trigger:

if(!TriggerHelper.processedMap.containsKey('AfterUpdate')){

    TriggerHelper.processedMap.put(
        'AfterUpdate',
        new Set<Id>()
    );

}

Set<Id> processedIds =
TriggerHelper.processedMap.get('AfterUpdate');

Now each trigger event maintains its own collection of processed records.

This approach works well in enterprise applications where one object supports several trigger events.

Method 5: Use a Trigger Handler Pattern

One of the best long-term solutions is using a Trigger Handler framework.

Instead of placing business logic directly inside the trigger, move everything into dedicated Apex classes.

Simple example:

Trigger:

trigger AccountTrigger on Account(

    before insert,
    before update,
    after update

){

    AccountTriggerHandler.run();

}

Handler Class:

public class AccountTriggerHandler{

    public static void run(){

        if(Trigger.isBefore && Trigger.isUpdate){

            handleBeforeUpdate();

        }

        if(Trigger.isAfter && Trigger.isUpdate){

            handleAfterUpdate();

        }

    }

    private static void handleBeforeUpdate(){

    }

    private static void handleAfterUpdate(){

    }

}

This architecture makes recursion prevention much easier because every trigger event is managed in one place.

If you’re already using Apex Switch Statements vs If-Else: When and How to Use Each, the handler class becomes even cleaner by switching on Trigger.operationType.

Developer workspace with Salesforce and Apex code
Developer workspace with Salesforce and Apex code

Trigger Frameworks Reduce Recursion

Large Salesforce projects almost always follow these principles:

  • One trigger per object
  • Handler classes
  • Service classes
  • Utility classes
  • Bulkified logic
  • Recursion protection
  • Minimal trigger code

Instead of placing hundreds of lines inside a trigger, each responsibility stays inside its own class.

This also makes debugging much easier.

Developers learning enterprise Apex should also read Apex Switch Statements vs If-Else: When and How to Use Each because clean control flow improves trigger readability.

Common Mistakes That Cause Recursive Triggers

Updating Records Inside After Update

This is the most common mistake.

Bad example:

update Trigger.new;

The update immediately fires the trigger again.

Ignoring Trigger.oldMap

Many developers update records without checking whether values actually changed.

Always compare old and new values whenever possible.

Using Multiple Triggers

Having several triggers on one object makes execution difficult to predict.

Salesforce recommends using one trigger per object.

Mixing Apex and Flow Without Planning

Today’s Salesforce orgs usually contain:

  • Apex
  • Record-Triggered Flow
  • Validation Rules
  • Managed Packages

Without proper planning, one automation can easily trigger another and create recursive loops.

Best Practices for Preventing Recursive Triggers

Follow these recommendations whenever writing Apex triggers:

  • Compare values using Trigger.oldMap before performing DML.
  • Prefer Static Set over Static Boolean.
  • Use Trigger Handler frameworks.
  • Bulkify every trigger.
  • Keep triggers logic-less.
  • Avoid updating the same object unnecessarily.
  • Test bulk operations using hundreds of records.
  • Review Flow automation together with Apex.

Following these practices produces scalable Apex code that remains maintainable as the org grows.

Advanced Scenario: Apex Trigger and Flow Causing Recursion

Modern Salesforce orgs rarely rely only on Apex. Most organizations also use Record-Triggered Flows, Validation Rules, Process Builder (legacy), and managed packages.

Imagine this sequence:

  1. An Account record is updated.
  2. The Apex trigger updates a related Contact.
  3. A Flow on Contact updates the Account.
  4. The Account trigger runs again.

Although your trigger code looks correct, the combination of Apex and Flow creates a recursive loop.

To avoid this:

  • Check field values before updating records.
  • Avoid unnecessary DML operations.
  • Coordinate Flow and Apex logic.
  • Use a Trigger Handler framework.
  • Document your automation design.

Testing Recursive Trigger Prevention

Writing recursion prevention code isn’t enough. You should also verify that it works under different scenarios.

A good test class should cover:

  • Single record updates
  • Bulk updates (200+ records)
  • Multiple trigger executions
  • Trigger.oldMap conditions
  • Static Set behavior
  • Cross-object updates

Example:

@isTest
private class AccountTriggerTest{

    @isTest

    static void testBulkUpdate(){

        List<Account> accounts = new List<Account>();

        for(Integer i = 0; i < 300; i++){

            accounts.add(
                new Account(
                    Name='Account ' + i
                )
            );

        }

        insert accounts;

        for(Account acc : accounts){

            acc.Phone = '1234567890';

        }

        Test.startTest();

        update accounts;

        Test.stopTest();

    }

}

Bulk testing helps ensure that recursion prevention techniques continue working even when Salesforce processes records in multiple batches.

Developers should also review How to Call Apex Imperatively in Salesforce LWC to improve code coverage and validate trigger behavior effectively.

Static Boolean vs Static Set vs Trigger.oldMap

Choosing the right approach depends on your business requirement.

MethodRecommendedBulk SafeEnterprise Ready
Static BooleanNoNoNo
Trigger.oldMapYesYesYes
Static SetYesYesYes
Static MapYesYesYes
Trigger Handler FrameworkHighly RecommendedYesYes

In most Salesforce projects, developers combine Trigger.oldMap, a Static Set, and a Trigger Handler Pattern to achieve the best balance between readability and recursion protection.

Frequently Asked Questions

What is a recursive trigger in Salesforce?

A recursive trigger is a trigger that repeatedly executes because it performs DML operations that fire the same trigger again during the same transaction.

Why are recursive triggers dangerous?

Recursive triggers can cause:

  • Infinite execution loops
  • Governor limit exceptions
  • Maximum trigger depth exceeded errors
  • Performance issues
  • Duplicate processing

Is a static Boolean still recommended?

No. A static Boolean may skip records during bulk operations because it only allows the first trigger execution within a transaction. Salesforce developers generally prefer Trigger.oldMap, Static Sets, or Trigger Handler frameworks instead.

What is the best way to prevent recursive triggers?

For most projects:

  • Compare values using Trigger.oldMap.
  • Use a Static Set for processed record IDs.
  • Implement a Trigger Handler framework.
  • Avoid unnecessary DML statements.

Can Salesforce Flow also cause recursion?

Yes.

Flows, Apex triggers, Process Builder, Workflow Rules, and managed packages can trigger each other if updates are not carefully controlled.

Should I update records inside an after update trigger?

Only when necessary.

If possible, perform field modifications in a before trigger or compare values before issuing DML operations.

Final Thoughts

Learning how to prevent recursive triggers in Salesforce Apex is one of the most valuable skills for any Salesforce developer. Recursive triggers may appear harmless during initial development, but they often become a major source of governor limit errors, duplicate processing, and difficult-to-debug automation issues in production environments.

Rather than relying on outdated techniques such as static Boolean variables, modern Salesforce development focuses on smarter approaches like Trigger.oldMap, Static Set, Static Map, and well-structured Trigger Handler frameworks. These patterns improve code readability, support bulk processing, and make your automation more reliable as your Salesforce org grows.

Whenever you build a new trigger, remember one simple principle: perform DML operations only when they are truly necessary. Combined with proper testing and clean architecture, this approach will help you write scalable Apex code that remains easy to maintain for years.

Developer workspace with Salesforce and VS Code (1)
Developer workspace with Salesforce and VS Code (1)

Related Articles

  • Apex Trigger Tutorial for Beginners in Salesforce
  • Apex Switch Statements vs If-Else: When and How to Use Each
  • Salesforce Governor Limits with Real Examples and Best Practices
  • How to Use lightning-record-edit-form in Salesforce LWC

TAGGED:ApexApex Best Practicesapex triggerGovernor LimitsRecursive Triggersalesforce adminsalesforce apexSalesforce Codingsalesforce developerTrigger.oldMap
Share This Article
Facebook Email Print
ByNeha Panwar
Salesforce Developer and Technical Writer
Follow:
Neha Panwar is a Salesforce developer and technical writer who shares practical tutorials, Apex guides, and real-world solutions for developers. She focuses on simplifying Salesforce concepts, integrations, and backend development to help beginners and professionals learn faster.
Leave a Comment Leave a Comment

Leave a Reply Cancel reply

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

Latest Post

Salesforce dynamic forms interface illustration
Salesforce Dynamic Forms: A Better Way to Show and Hide Fields
Salesforce Flow
Salesforce flow debugging guide
How to Debug and Fix Salesforce Flow Errors ?
Salesforce Flow
Salesforce Flow Loops tutorial diagram
Salesforce Flow Loops: Collections, Iteration, and Best Practices
Salesforce Flow
WhoId vs WhatId in Salesforce
WhoId vs WhatId in Salesforce: What’s the Difference and When Should You Use Each?
Apex Development
Salesforce Apex string methods tutorial
20 Apex String Methods Every Salesforce Developer Should Know
Apex Development

Stay Updated with Salesforce Tutorials

Get the latest Salesforce guides, tutorials, and developer tips delivered to your inbox.
slaesforce corner mascot

Explore More Topics

  • salesforce admin
  • salesforce developer
  • Salesforce Admin
  • salesforce apex
  • Salesforce Development
  • salesforce tutorial
  • salesforce security
  • salesforce automation
  • Apex Development
  • lightning web components
  • Lightning Web Components
  • salesforce lwc
  • Salesforce Tutorials
  • Salesforce Tools
  • lwc tutorial
horizontal-dark-transparent

Learn Salesforce development with practical tutorials, Apex guides, integration examples, and real-world solutions for developers.

  • Quick Links:
  • About
  • Contact
  • Privacy Policy
  • Disclaimer
  • Terms & Conditions
Facebook Twitter Youtube Linkedin-in

Salesforce Corner © 2026

Welcome Back!

Sign in to your account

Username or Email Address
Password

Lost your password?