Apex Triggers Cheatsheet
Apex Triggers Cheatsheet
Rajaganesh Saravanan
The trigger is an Apex Code that executes before or after changes occurs to Salesforce records.
These changes includes operations like Insert, Update, Delete, Merge, Upsert and Undelete.
deleted, or undeleted. If any change happens to a single or multiple records, the Apex Trigger
3. Use triggers to perform tasks that can’t be done using a point & click tool in Salesforce. If the
task is possible using point & click tools then always prefer doing it from them.
4. Triggers are active by-default when created and Salesforce automatically fires active triggers
database.
2. After Triggers: These are used to access fields values that are set by the system like recordId,
lastModifiedDate field. Also, we can make changes to other records in the after trigger but not on
the record which initiated/triggered the execution of after trigger because the records that fire
//code-block
where trigger-events can be a comma-separated list of one or more of the following events:
● Before insert
● Before update
● Before delete
● After insert
● After update
● After delete
● After undelete
Example:
For example:
Update Account a;
Inserts contact b;
}
Note: If you update or delete a record in its before trigger, or delete a record in its after trigger you’ll
receive an error and this includes both direct and indirect operations.
Salesforce loads or initializes the record from the database. Salesforce also loads
the new record with the provided new values by overriding the old values.
Salesforce identifies the source of the request. The source can be standard UI
pages, custom UI pages, SOAP Api or REST api. Based on the source of the request,
different validation checks are performed.
Salesforce executes Record Triggered flows which are created to run before the
record is saved.
Salesforce executes all the Before triggers i.e. before insert or before update or
before delete triggers. So, any logic written in the Before triggers will execute at
this stage.
Once the Before Triggers have been executed, Salesforce system validation
mechanisms execute all the system and custom validations.
After the Duplicates rules, Salesforce will Save the record but will not commit it to
the database. Record will only be committed when the record in context has
successfully completed the order of execution without any failure.
After the record is saved, Salesforce executes any After Triggers i.e. after insert,
after update, after delete So, any logic written in the After triggers will execute at
this stage
If the record is an object that supports assignment rules, Salesforce executes the
Assignment Rules at this stage. Assignment rules specify how records should be
assigned to users or queues. These rules can be based on various criteria, such as
record type, region, or priority.
If the record is an object that supports Escalation Rules, Salesforce executes the
Escalation Rules at this stage. Escalation rules specify how records should be
escalated to higher authorities, such as managers or executives if they are not
processed within a certain time frame.
Stage 13: Execute Flows
If the record is an object that supports Lightning Flows and Processes, Salesforce
executes them at this stage. The order in which Flows will execute is as per system
behavior and your configuration.
At this stage, Salesforce runs the record-triggered flows which are created to run
after the record is saved.
If the record is an object that supports Entitlement Rules, Salesforce executes the
Entitlement Rules at this stage.
If the parent record or grand parent records contain rollup summary fields, then
once the value is calculated the record goes through the save procedure cycle
again.
At this stage, Salesforce will check and execute all the Criteria Based Sharing Rules
for all the records in context to make sure the records are shared correctly.
After finishing all the above events on the server, Salesforce will finally commit the
record in the database.
Once all the previous stages have been completed, Salesforce executes any Post-
Commit Logic. Post-Commit Logic includes any custom code blocks developers
have created to run after a record has been committed to the database. These
code blocks can perform various actions, such as generating reports, updating
external systems, or sending notifications, sending emails, invoking async jobs, etc.
Context Variables
Trigger.New contains all the records that were inserted in insert or update triggers.
Trigger.Old provides the old version of sObjects before they were updated in update triggers, or a list
of deleted sObjects in delete triggers
➔ You can use an sObject to change its own field values using trigger.new but only in before triggers.
➔ Upsert and merge events do not have their own triggers, instead they fire other triggers based on
consume less server resources, and are less likely to exceed platform limits.
The benefit of bulkifying your code is that bulkified code can process large
numbers of records efficiently and run within governor limits on the Lightning
Platform. These governor limits are in place to ensure that runaway code
Not Bulk
Account a = Trigger.New[0];
for(Account a : Trigger.New) {
Use this to avoid Query limits which are 100 SOQL queries for synchronous Apex or
Bad Practice
for(Account a : Trigger.New) {
Good Practice
for(Account a : acctsWithOpps) {
trigger to fire, the trigger fires twice, once for each 200 records. For this reason, you
don’t get the benefit of SOQL for loop record batching in triggers, because triggers
batch up records as well. The SOQL for loop is called twice in this example, but a
standalone SOQL query would also be called twice. However, the SOQL for loop still
Bulk DML
Use this because the Apex runtime allows up to 150 DML calls in one transaction.
Bad Practice
update opp;
Good Practice
Store records to a list first before updating. Don't update per record.
oppsToUpdate.add(opp);
Instead of checking an opportunities list size for each record in the loop, add the
Iterate over accounts that are in this trigger but that don't have
opportunities.
StageName='Prospecting',
CloseDate=System.today().addMonths(1),
AccountId=a.Id));
if (oppList.size() > 0) {
insert oppList;
Scenario: When the Parent record is updated, update specific fields on related Child records.
Solution:
Use an after update trigger on the Parent object to update all related Child records in a
single DML statement.
Avoid querying and updating within nested loops.
Example: Let's say you have an Account (Parent) object and a related Contact (Child) object.
When an Account's Industry field is updated, you want to update the Industry__c field on all
related Contact records.
apex
Copy code
if (acc.Industry != Trigger.oldMap.get(acc.Id).Industry) {
accountIds.add(acc.Id);
if (!accountIds.isEmpty()) {
for (Contact con : [SELECT Id, AccountId FROM Contact WHERE AccountId IN
:accountIds]) {
con.Industry__c = Trigger.newMap.get(con.AccountId).Industry;
contactsToUpdate.add(con);
if (!contactsToUpdate.isEmpty()) {
update contactsToUpdate;
Best Practices:
Scenario: When a new Parent record is inserted, automatically create Child records
associated with it.
Solution:
In an after insert trigger, gather the Parent record data, then use a bulk insert for the
related Child records.
Example: Suppose each new Account should have two associated Contact records by default.
apex
Copy code
}
if (!contactsToInsert.isEmpty()) {
insert contactsToInsert;
Best Practices:
Avoid creating Child records in a loop by using a single bulk insert statement after
collecting the records.
Ensure trigger logic is bulkified to handle multiple Parent records.
Scenario: When a Child record is updated, update specific fields on the related Parent record.
Solution:
Use a Map<Id, ParentObject> to update the Parent only once per Parent record, even if
multiple Child records trigger the update.
Example: Imagine a Contact (Child) update that should modify the related Account (Parent)
field Has_Active_Contacts__c.
apex
Copy code
if (con.IsActive__c != Trigger.oldMap.get(con.Id).IsActive__c) {
if (!accountsToUpdate.isEmpty()) {
update accountsToUpdate.values();
Best Practices:
Scenario: Implement a roll-up summary where the Parent and Child are not in a Master-Detail
relationship.
Solution:
Use a trigger on the Child object to calculate values and store them in a Map<Id,
AggregateValue>.
Update the Parent record once per unique Parent ID.
Example: If you want to count the total Active_Contacts__c on an Account based on related
Contact records:
apex
Copy code
trigger ContactAfterInsertUpdateDelete on Contact (after insert, after update, after delete,
after undelete) {
for (Contact con : [SELECT AccountId FROM Contact WHERE AccountId != NULL]) {
accountContactCountMap.put(con.AccountId,
accountContactCountMap.getOrDefault(con.AccountId, 0) + 1);
if (!accountsToUpdate.isEmpty()) {
update accountsToUpdate;
Best Practices:
Solution:
1. Static Variables: Use a static variable in a helper class to track if the trigger has already
run.
2. Trigger Context Variables: Use Trigger.isBefore and Trigger.isAfter conditions to control
which actions run at each stage.
3. Custom Settings: Use custom settings to manage recursion limits for more complex
scenarios.
Example:
apex
Copy code
if (TriggerHandler.isTriggerExecuted) {
return;
TriggerHandler.isTriggerExecuted = true;
Best Practices: