Apex Triggers the Definitive Guide

Updated: Dec 20, 2021

Apex Triggers execute before or after a database insert, update, delete, merge, upsert, undelete, or any combination thereof. Salesforce.com allows Triggers for most standard and all custom objects if running Unlimited, Developer, Enterprise, or Database.com editions of Salesforce.com.

Apex Triggers allow developers to programmatically check for duplicate records, update fields across objects, automate the creation of new records based on criteria, and much more. For example, if I wanted to check for duplicity on the user email field every time a user was created or updated I would write a trigger and assign the "before insert, before update" operations to it. This would allow me to query the database and throw an exception if a duplicate is found before saving to the database.


trigger triggerName on Object( operation/s ) {
/* -- Execute Trigger Logic Here -- */

Governor Limits

Salesforce.com is a multi-tenant environment. In an effort not to jeopardize the availability of shared resources, Salesforce.com enforces governor limits which will return a runtime exception and the code executing will fail. For details on all Salesforce.com governor limits refer to Winter 12' Governor Limits Quick Reference Guide.

Apex Governor Limits

Apex Triggers are bound by the Apex governor limits. As of Salesforce.com's Winter 12 Release they are as follows:

DescriptionLimitTotal number of SOQL queries issued100Total number of SOQL queries issued for Batch Apex and future methods200Total number of records retrieved by SOQL queries50,000Total number of SOSL queries issued20Total number of records retrieved by a single SOSL query200Total number of DML statements issued150Total number of records processed as a result of DML statements, Approval.process, or database.emptyRecycleBin10,000Total number of executed code statements200,000Total number of executed code statements for Batch Apex and future methods1,000,000Total heap size3 MBTotal heap size for Batch Apex and future methods6 MBTotal stack depth for any Apex invocation that recursively fires triggers due to insert,update, or delete statements16For loop list batch size200Total number of callouts (HTTP requests or Web services calls) in a request10Maximum timeout for all callouts (HTTP requests or Web services calls) in a request120 secondsDefault timeout of callouts (HTTP requests or Web services calls) in a request10 secondsTotal number of methods with the future annotation allowed per Apex invocation10Maximum size of callout request or response (HTTP request or Web services call)3 MBTotal number of sendEmail methods allowed10Total number of describes allowed100Total number of test classes that can be queued per a 24–hour periodThe greater of 500 or 10 multiplied by the number of test classes in the organization

Best Practices

The Force.com platform provides developers the flexibility to write and easily deploy Apex Triggers. Apex Triggers, like any powerful tool, need to be written with solid logic and thoroughly tested before deploying into a production environment.

  • Use one Apex Trigger for every object; all logic should be handled by an external Apex Class.

  • Keep Apex Trigger logic simple, and thoroughly documented with code comments.

  • Write Test Classes that execute the code in realistic scenarios.

  • Write Apex Triggers for bulk processing (up to 200) records at a time.

  • DML statements should execute on collections not individual records.

  • Add field validations on both the before and after operations.

  • Use static variables to persist on both the before and after operations.

  • Do not execute SOQL queries inside for loops, or inside of any before or after operations.

Design Pattern

Utilize consistent design patterns for Apex Triggers as you would any other code and the code will be more likely to run efficiently, within governor limits, and bug-free.

Example Trigger

An Apex Trigger should be written so that every operation calls a method in an external Apex Class. By doing so code can easily be added or removed, and variables can be used multiple times without additional queries to the database.


trigger objectTrigger on Object (after delete, after insert, after undelete, after update, before delete, before insert, before update) {

  objectTriggerHandler handler = new objectHandler();

  /* Before Insert */
  if(Trigger.isInsert && Trigger.isBefore){
  /* After Insert */
  else if(Trigger.isInsert && Trigger.isAfter){
  /* Before Update */
  else if(Trigger.isUpdate && Trigger.isBefore){
    handler.OnBeforeUpdate(Trigger.old, Trigger.new, Trigger.newMap);
  /* After Update */
  else if(Trigger.isUpdate && Trigger.isAfter){
    handler.OnAfterUpdate(Trigger.old, Trigger.new, Trigger.newMap);
  /* Before Delete */
  else if(Trigger.isDelete && Trigger.isBefore){
    handler.OnBeforeDelete(Trigger.old, Trigger.oldMap);
  /* After Delete */
  else if(Trigger.isDelete && Trigger.isAfter){
    handler.OnAfterDelete(Trigger.old, Trigger.oldMap);

  /* After Undelete */
  else if(Trigger.isUnDelete){


Example Trigger Handler Class

The Apex Trigger template above calls a handler class to execute the trigger logic, the trigger handler class should be defined as follows:


public with sharing class ObjectTriggerHandler {

  private boolean m_isExecuting = false;

  public ObjectTriggerHandler(boolean isExecuting){
    m_isExecuting = isExecuting;

  public void OnBeforeInsert(Object[] newObjects){

  public void OnAfterInsert(Object[] newObjects){

  public void OnBeforeUpdate(Object[] oldObjects, Object[] updatedObjects, MapObjectMap){

  public void OnAfterUpdate(Object[] oldObjects, Object[] updatedObjects, MapObjectMap){

  public void OnBeforeDelete(Object[] ObjectsToDelete, MapObjectMap){

  public void OnAfterDelete(Object[] deletedObjects, MapObjectMap){

  public void OnUndelete(Object[] restoredObjects){

  public boolean IsTriggerContext{
    get{ return m_isExecuting;}

Recursive Triggers

Prevent Apex Triggers from running recursively and thus exceeding governor limits, include the following statement to the Apex Trigger handler class.


public static boolean firstRun = true;

Insert the following into the OnAfterUpdateMethod.
if (firstRun) {
  firstRun = false;
else {
  System.debug('Already ran!');

Many excellent contributions by experienced Salesforce.com developers made this article possible. Please use the references to their sites and related blog posts below for more information. Happy Coding!

Other Resources

  • Mike Leach: A Simple Trigger Template for Salesforce (2010/07/08)

  • Ray Dehler:Prevent an Apex Trigger from Executing Twice (2011/04/13)

  • Jeff Douglas: Force.com Programming Best Practices (2010/10/21)

  • Developer Force - Apex Code Best Practices

17 views0 comments

Recent Posts

See All

Emergency Fund 401K Match High Interest Debt Roth IRA HSA 529 Max 401K Taxable Low Interest Debt Mortgage https://www.youtube.com/watch?v=UfR9WqxDhGk

Dan teaches courses on pluralsight, but he also maintains two sites: 1. A google custom search engine for all things SalesForce https://searchtheforce.com/ 2. advanceapex.com