Mastering Triggers for Interviews
Mastering Triggers for Interviews
Trigger.isBefore - Boolean
Trigger.isAfter - Boolean
Trigger.isInsert - Boolean
Trigger.isUpdate - Boolean
Trigger.isDelete - Boolean
Trigger.IsUndelete - Boolean
Trigger.new - List
Trigger.Old - List
Trigger.newMap - Map
Trigger.OldMap- Map
Trigger.OperationType -Enum
Trigger.Size - Integer
Trigger.isExecuting - Boolean
Trigger.Newmap
UPDATE Trigger.New Trigger.New
Trigger.Newmap Trigger.Newmap
Trigger.Old Trigger.Old
Trigger.OldMap Trigger.OldMap
UNDELETE Trigger.New
Trigger.Newmap
Before After
Create New Y Y
Record(Related/Non
Related)
Sending Y
Email/Custom
Notification
Example: Use Flow to automatically send a welcome email to a new Contact after
they’re created.
1. Complex Logic: When you need advanced logic that flows can’t handle, such as
multiple conditional checks, loops, or calculations across related objects.
2. Bulk Processing: For handling bulk updates or inserts, where large amounts of
records need processing at once (e.g., data migrations or mass updates).
3. Cross-Object Actions: When you need to update records on unrelated objects
or perform actions on objects not directly related to each other.
4. Real-Time Processing: For faster, real-time processing where performance is
critical.
1. Static Variables
2. Static Set of IDs (better for handling more than 200 records)
Here's Why:
● With a static variable, only the first 200 records are processed.
● The additional records (like the remaining 10 in our example) won’t be processed, and no
error will be thrown, which can lead to incomplete processing!
Common Errors
Additional Notes
● System Validation: Fields like Name, Mobile, Email have character limits.
● Synchronous Execution: Triggers process records in a batch size of 200.
● External Callouts: Use Future methods for callouts as triggers are synchronous.
Best Practice
1. One Trigger per Object (Control execution order)
2. Implement business logic in a Trigger Handler
3. Always bulkify for single/multiple records
4. Avoid nested loops ➡Use Maps instead
5. Control recursion carefully
6. Document code thoroughly
7. Before deployment, remove unused code and debug statements
8. Unit Test with bulk scenarios using test classes
9. Master Trigger Context Variables (like Trigger.New, Trigger.Old, etc.)
Here's a more structured version of the syntax and example code for a Trigger
Handler in Salesforce, with comments to guide through each part:
// Declare variables for the current and old records List<Account> triggerNew;
List<Account> triggerOld; Map<Id,
Account> triggerNewMap; Map<Id,
Account> triggerOldMap;
// Main method to handle actions based on the trigger operation type public void doAction() {
// Switch statement for handling different trigger operations switch on
Trigger.operationType {
when BEFORE_INSERT {
onBeforeInsert();
}
when AFTER_INSERT {
onAfterInsert();
}
when BEFORE_UPDATE {
onBeforeUpdate();
}
when AFTER_UPDATE {
onAfterUpdate();
}
when BEFORE_DELETE {
onBeforeDelete();
}
when AFTER_DELETE {
onAfterDelete();
}
when AFTER_UNDELETE {
onAfterUndelete();
}
}
}
This trigger references the AccountTriggerHandler class to execute actions based on the
specified events.
Apex Trigger
trigger AccountTrigger on Account (before insert, after insert, before update,
after update, before delete, after delete, after undelete) {
● Benefits: This pattern keeps your code modular, making it easier to maintain and scale. It
also allows for better error handling and improved readability.
● Bulkification: Ensure that each method inside the handler class processes records in bulk
to prevent hitting governor limits.
● Trigger Operations: Use Trigger.operationType to determine the context in which the
trigger is running (insert, update, delete, etc.).
● Testing: For each method in the handler, write comprehensive test classes covering
different scenarios to ensure that your triggers run smoothly with bulk data.
Examples For Each Method
Before Insert
Scenario: Set rating hot when account created with industry as banking.
Apex Trigger
trigger AccountTrigger on Account (before insert) { AccountTriggerHandler handler = new
AccountTriggerHandler(); handler.doAction();
}
Here are the best practices followed in the above code, each explained in a single line:
Before Delete
Scenario: Leads with a status of 'Working Contacted' should not be deleted
under any circumstances.
}
}
Trigger
trigger LeadTrigger on Lead (before delete) { LeadTriggerHandler handler = new
LeadTriggerHandler(); handler.doAction();
}
After Insert
Scenario: When a Opportunity Created Create a Task for Follow up
}
}
// Loop through each Opportunity and create a Task for follow-up for (Opportunity
record : opportunityList) {
Task taskRecord = new Task();
taskRecord.WhatId = record.Id;
taskRecord.OwnerId = record.OwnerId; // Owner of the
Opportunity
taskRecord.Subject = 'Follow up';
taskList.add(taskRecord);
}
Trigger
trigger OpportunityTrigger on Opportunity (after insert, before update, after update) {
OpportunityHandler obj = new OpportunityHandler(); obj.doAction();
}
After Update
Scenario: When an account is marked as inactive then close all the cases if any.
Trigger
trigger AccountTrigger on Account (after update) {
AccountHandler obj = new AccountHandler();
obj.doAction();
}
After Delete
Scenario: When a Department record is deleted, its related Employee records
should also be deleted.
when AFTER_DELETE {
onAfterDelete();
}
}
}
Trigger
trigger DepartmentTrigger on Department c (before delete, after delete) {
DepartmentTriggerHandler handler = new DepartmentTriggerHandler();
handler.doAction();
}
Note: Using static for employee List allows the variable to be shared consistently within the
same transaction, preserving data integrity across trigger executions, preventing redundant
processing, and safeguarding against unintended modifications. This approach is particularly
valuable in Salesforce’s bulk processing environment, where efficiency and adherence to
governor limits are critical.
Undelete
employeesToRestore.add(employee);
}
// Insert recreated Employee records and handle any errors try {
if (!employeesToRestore.isEmpty()) {
insert employeesToRestore;
}
} catch (DmlException e) {
System.debug('Exception while restoring employees: ' + e.getMessage());
}
}
}
Trigger
trigger DepartmentTrigger on Department c (after undelete) {
DepartmentTriggerHandler handler = new
DepartmentTriggerHandler();
handler.onAfterUndelete();
}
Notes
Another way
Apex Trigger Handler
public class DepartmentTriggerHandler {
when AFTER_UNDELETE {
onAfterUnDelete();
}
}
}
try {
// If there are deleted Employee records, undelete them if
(!empList.isEmpty()) {
undelete empList;
}
} catch (DmlException e) {
System.debug('Exception while undeleting employees: '
+ e.getMessage());
}
}
}
Trigger
trigger DepartmentTrigger on Department c (after undelete) {
DepartmentTriggerHandler handler = new
DepartmentTriggerHandler();
handler.onAfterUnDelete();
}
Rollup Summary
Scenario: When Opportunity stage changed into negotiation/review then update the
opportunities count in the Account Object
}
}
}
public static void updateCountOfOpportunity()
{
//1. Collect Unique Parent Ids Set<Id>
accountIds=new Set<Id>(); for(Opportunity
oppRecord:triggernew)
{
if(oppRecord.AccountId!=null)
{
//1. update - Compare Old and new values
//2. Insert, Delete, Undelete - No need to compare if(Trigger.isupdate)
{
if(Trigger.OldMap.get(oppRecord.Id).StageName!=oppRecord.StageName)
accountIds.add(oppRecord.AccountId);
}
else
{
accountIds.add(oppRecord.AccountId);
}
}
}
Recursive Handler
Scenario Overview:
You have the following set up:
1. Flow: Automatically sets Rating = Hot on an Account when Customer Priority = High.
2. Before Update Trigger: Checks if the Account Type is updated to Customer - Direct. If
true, it sets Customer Priority = High.
3. After Update Trigger: Creates a follow-up task for the Account if certain criteria are
met.
1. Initial Update to Account Record: An update to the Account record occurs (e.g.,
changing Type to Customer - Direct).
2. Before Update Trigger (First Run):
○ The trigger detects that Type is set to Customer - Direct, so it sets Customer
Priority = High.
3. After Update Trigger (First Run):
○ Since Customer Priority is now High, the After Update Trigger creates a follow-up
task for this Account.
4. Flow (After-Save):
○ The Flow detects Customer Priority = High and sets Rating = Hot on the Account.
5. Before and After Triggers Execute Again:
○ The Flow’s update to Rating = Hot causes Salesforce to re-run the Before Update
and After Update triggers for this record.
○ The Before Update Trigger might again try to set Customer Priority = High, and
the After Update Trigger might attempt to create another follow-up task.
6. Infinite Loop:
○ The combination of the Flow update and the triggers can create a loop where
Salesforce repeatedly updates the Account and creates tasks in an endless cycle.
Recursive Handler
Trigger
// After Update Trigger: Create follow-up Task if criteria are met and avoid recursion
if (trigger.isAfter && trigger.isUpdate) { List<Task> taskList = new
List<Task>();
Interview Questions
A Salesforce trigger is a component of Apex code that runs before or after specified data
manipulation language (DML) events, such as before object records are inserted into the
database, even after records are deleted, and so on. Triggers are used to execute custom actions
before or following changes to Salesforce records.
A Trigger code in Salesforce is an element of Apex code that runs before or after particular data
manipulation language (DML) events. These events include Salesforce records insert, update,
deletes, and undelete. The trigger code is used to implement custom logic, such as data
validation, automation, or modification, in response to various DML events.
Before Triggers: These are called before the DML process on the database is completed. They
are commonly used to validate or change data before it is saved.
After Triggers: These triggers are executed after the DML operation and data temporarily
saved into the database. They can be used when accessing system-set field values (such as a
recordId or LastModifiedDate field) or modifying other documents based on the initial record’s
actions.
Trigger Old: It has the list of old records values before they were updated or deleted.
Yes, triggers should always be written with bulk processing in mind, meaning they should
handle multiple records at once. Using loops or SOQL Queries inside loops can cause issues
with governor Limits so developers need to optimize triggers for bulk operations.
A recursive trigger shows up when a trigger calls itself, that leads to an infinite loop. You can
avoid recursion by using a static boolean variable to track whether the Trigger has already run.
8. What is the use of Trigger.isExecuting?
Trigger.isExecuting is a boolean that returns true if the current context is a trigger, that will
eventually help to check whether your code is running in a trigger context.
Trigger.new is a list of records with new values. On the other hand, Trigger.New.Map is a map
of IDs to records. This is useful when accessing records using their IDs for processing.
Yes, you can use DML operations in triggers. However, the best practice is to limit the use of
DML operations to avoid hitting Salesforce governor limits. Using collections to handle bulk
records in DML is recommended.
You can utilize static variables to prevent a trigger from being executed more than once. A static
variable serves as a flag. You can set this flag after the Trigger has been executed. You can skip
the logic on subsequent recursive calls to the Trigger by checking the flag’s value.
This process ensures that the Trigger is not executed repeatedly within the same transaction,
preventing undesirable behavior or failures.
Avoid performing DML actions (insert, update, and delete) or SOQL searches within loops to
ensure your triggers are bulk-safe. This can soon exceed Salesforce governor restrictions,
mainly when dealing with many records. Alternatively, you should:
So, Trigger can handle mass activities efficiently without experiencing performance difficulties.
14. What are context variables in Salesforce Triggers?
context variables in Salesforce Triggers give critical information about the state of the records
being processed and the runtime context in which the Trigger is executed. Some standard
context variables are:
Trigger. New: Contains the most recent versions of the records inserted or changed.
Trigger. Old: Contains previous versions of the records being updated or destroyed.
Trigger.isInsert, Trigger.isUpdate, and Trigger.Delete: Indicate the type of DML activity that
prompted the Trigger to run.
Trigger.isBefore, Trigger.isAfter: Determine whether the Trigger executes before or after the
DML action.
These variables enable developers to handle multiple scenarios efficiently within a single
Trigger.
Salesforce platform permits you to have numerous Triggers on the same object. However, the
hierarchy in which the various triggers are executed is not guaranteed. This can lead to
unpredictability in how the triggers interact.
Salesforce recommends that each item have only one Trigger to avoid potential complications.
A Trigger Handler class allows you to regulate the sequence and execution of your logic easily.
To test Triggers in Salesforce, you write test classes and methods. In the test methods, you
create test data and perform DML operations that invoke the Trigger. You also use
System.assert methods to verify the Trigger’s behavior.
While it’s not recommended due to potential governor limit issues, you can call a batch class
from a Trigger using the Database.executeBatch method. A better practice is to use a queueable
or future method to handle asynchronous processing.
18. When would you use a Trigger instead of Workflow,
Process Builder, or Flows?
You would use a Trigger instead of Workflow, Process Builder, or Flows when dealing with
complex business logic that cannot be handled by these declarative tools or when you need to
create or manipulate records related to the one being processed.
Exceptions in Salesforce Triggers can be handled using try-catch blocks. In the try block, you
write the code which might throw an exception, and in the catch block, you handle the
exception. This can involve adding an error message to the record or logging the error for
review.
The purpose of a Trigger handler class is to separate the logic of your Trigger from the Trigger
itself. This separation leads to code that is easier to maintain and test. The handler class contains
the methods that carry out the operations needed when the Trigger fires.
If a Trigger causes a runtime exception, the entire transaction is rolled back. This includes all
DML operations and changes in governor limit usage.
To debug a Trigger in Salesforce, you can use debug logs. You can add System.debug
statements in your Trigger code, and then check the logs after executing the operations that fire
the Trigger.
23. What are the limitations of using Triggers in
Salesforce?
● Triggers are not suitable for time-consuming operations as they can hit governor limits.
● They can lead to recursion if not handled properly.
● Triggers run on all records of a batch operation, so selective processing can be
challenging.
● Debugging Triggers can be difficult, particularly in production.
● Overutilization of Triggers can lead to complex order of execution scenarios.
Yes, SOQL queries can be used in a Trigger, but it’s important to follow best practices to avoid
hitting governor limits. These include bulkifying the Trigger to handle multiple records
efficiently, avoiding SOQL queries inside loops, and ensuring that the total number of SOQL
queries does not exceed the limit in a single transaction. Using collections to store data and
querying outside of loops is recommended