At work we are working on a new Salesforce organization. We’ve written some code over the last few months, but at the advice of some contractors who are more seasoned with Apex, we are doing some refactoring of the code. One of these changes is making sure that recursive triggers are not possible – something along the lines of Account AFTER INSERT being called, which updated a Contact, which in turn might update the Account again.

We were a little stumped as to why this was necessary. I have been working in Apex on and off for six years now, but I never dug deep enough to understand what happens when nested triggers are called on different objects in the same transaction. So, I spun up a clean dev org to figure out how this works. In particular, I wanted to see if static variables persisted between the different triggers executing. Say you had the following:

  1. An Account is created, which calls the BEFORE INSERT trigger for the Account
  2. The trigger for the Account causes a Contact to be created. (This is a bad example, but the Contact is not tied to the account. In order to do so, this would be done in the AFTER INSERT trigger)
  3. The Contact is inserted, which calls the Contact BEFORE INSERT trigger.

It turns out that any static state that is done in the handler for the Account trigger, is still available to the Contact handler.

trigger AccountTrigger on Account (before insert, after insert) {
    if (Trigger.isBefore) {
        new AccountHandler().before('AT');
    }
    if (Trigger.isAfter) {
        new AccountHandler().after('AT');
    }
}
trigger ContactTrigger on Contact (before insert, after insert) {
    if (Trigger.isBefore) {
        new ContactHandler().before('CT');
    }
    if (Trigger.isAfter) {
        new ContactHandler().after('CT');
    }
}
public class AccountHandler {
    public void before(String caller) {
        TriggerHandler.pushit('AH.before('+caller+')');
        List<Contact> contacts = new List<Contact>();
        for(sObject s : Trigger.new) {
            Account a = (Account) s;
            contacts.add(new Contact(FirstName=a.Name, LastName=a.Name, Email =a.Name +'@example.com'););
        }
        insert contacts;
    }
    
    public void after(String caller) {
        TriggerHandler.pushit('AH.after('+caller+')');
    }
}
public class ContactHandler {
    public void before(String caller) {
        TriggerHandler.pushit('CH.before(' + caller+')');
    }
    
    public void after(String caller) {
        TriggerHandler.pushit('CH.after('+caller+')');
    }
}
public class TriggerHandler {
    public static List<String> strings = new List<String>();
    public static void pushit(String s) {
        strings.add(s);
        System.debug('After adding ' + s +', strings now contains: ' + strings);
    }
}

The results, after inserting a single Account:

After adding AH.before(AT), strings now contains: (AH.before(AT))
After adding CH.before(CT), strings now contains: (AH.before(AT), CH.before(CT))
After adding CH.after(CT), strings now contains: (AH.before(AT), CH.before(CT), CH.after(CT))
After adding AH.after(AT), strings now contains: (AH.before(AT), CH.before(CT), CH.after(CT), AH.after(AT))