Table of Contents
Salesforce Triggers is an essential term to focus on in your Salesforce development journey. Triggers are necessary to automate the business processes within the Salesforce platform, and there is a high probability that interviewers might ask you questions related to this topic. If you are planning to build a successful career as a Salesforce developer, learning about this topic would be beneficial.
We appreciate your zeal to learn new things, so we have prepared the most popular Salesforce Trigger interview questions with answers in this blog. We have covered the basic concepts and advanced scenario-based questions to prepare you for the interview. So, let’s explore these questions and get prepared to pave your way to success.
Salesforce Trigger Interview Questions for Beginners
1. What is a Salesforce trigger?
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 execute custom actions before or after changes to Salesforce records.
2. What is a Trigger Code?
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 insert, update, delete, and undelete operations on Salesforce records.
The trigger code is used to implement custom logic, such as data validation, automation, or modification, in response to various DML events.
Trigger Code Example:
trigger AccountTrigger on Account (before insert) {
for(Account acc : Trigger.new) {
// Add "Customer -" before Account Name
acc.Name = 'Customer - ' + acc.Name;
}
}
3. What are the types of Salesforce Triggers?
There are two types of triggers in Salesforce:
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 execute after the DML operation, and the data is temporarily stored in the database. They can be used when accessing system-set field values (such as a recordID or LastModifiedDate) or when modifying other documents based on the initial record’s actions.
4. Can you explain the trigger execution order in Salesforce?
Salesforce executes triggers in the following order:
- Record is Saved (User clicks Save or DML operation runs).
- Loads original record (or initializes new record).
- System Validation Rules (Checks required fields, data types, field lengths).
- Executes BEFORE Triggers (Apex ‘before insert’ or ‘before update’).
- Executes Custom Validation Rules & Layout Validation.
- Executes Duplicate Rules.
- Saves Record to Database (Temporary state: NOT committed yet, no ID visibility to other users).
- Executes AFTER Triggers (Apex ‘after insert’ or ‘after update’).
- Executes Assignment Rules.
- Executes Auto-Response Rules.
- Executes Workflow Rules.
- Executes Record-Triggered Flows & Process, Builders.
- Executes Escalation Rules.
- Executes Entitlement Rules.
- Calculates Roll-up Summary Fields & Cross-Object Formulas.
- Commits Changes to Database (Data is now permanent and visible to all users).
5. What is the Trigger.new and Trigger.old context variable?
Trigger.new: It holds the list of new records to be inserted or updated.
Trigger.old: It has the list of old record values before they were updated or deleted.
6. Can triggers be bulkified in Salesforce?
Yes, triggers should always be written with bulk processing in mind, meaning they should handle multiple records at once. Using loops or SOQL queries within loops can exceed the governor limit, so developers need to optimize triggers for bulk operations.
7. What are recursive triggers, and how can you avoid them?
A recursive trigger occurs when a trigger reinvokes itself, leading to an infinite loop. You can avoid recursion by using a static boolean variable to track whether the Trigger has already run.
public class TriggerRecursionHandler {
// Static variable to track the first execution
public static Boolean isFirstRun = true;
}
trigger AccountTrigger on Account (after update) {
if (TriggerRecursionHandler.isFirstRun) {
// Set the flag to false to prevent recursion
TriggerRecursionHandler.isFirstRun = false;
// trigger logic
List<Account> accountsToUpdate = new List<Account>();
// ... build your list and perform DML ...
update accountsToUpdate;
}
}
8. What is the use of Trigger.isExecuting?
Trigger.isExecuting is a context variable in Salesforce Apex that returns true when the current code is running within a trigger context. It is used to determine whether the code is being executed from a trigger or another Apex source, such as a class, batch job, or anonymous block. It helps developers control and differentiate logic based on the execution context.
9. State the difference between Trigger.new and Trigger.newMap.
Trigger.new is a list of records with new values. On the other hand, Trigger.newMap is a map of IDs to records. It is useful when processing records using their IDs.
10. Can you use DML operations in triggers?
Yes, you can use DML operations in triggers. However, best practice is to limit DML operations to avoid hitting Salesforce governor limits. Using collections to handle bulk records in DML is recommended.
Salesforce Trigger Interview Questions for Professionals
11. How would you stop a Trigger from executing multiple times?
You can use static variables to prevent a trigger from executing 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.
12. Can you explain the concept of a Trigger framework? Why is it used?
The trigger framework is a Salesforce Apex design pattern that enables more modular and organized management of trigger logic. It usually entails using a handler class that includes the logic, with the Trigger calling the relevant function from that class. The primary advantages of adopting a trigger framework are:
- Reduces code duplication.
- Improves code reuse.
- It simplifies the trigger logic by keeping the code clean and maintainable.
13. How do you ensure that your Triggers are bulk-safe?
Avoid performing DML actions (insert, update, and delete) or SOQL searches within loops to ensure your triggers are bulk-safe. It can soon exceed Salesforce governor restrictions, mainly when dealing with many records. Alternatively, the best practices for creating bulk-safe triggers include:
- Using collections such as Lists, Sets, and Maps
- Performing SOQL queries outside loops
- Performing DML operations outside loops
- Processing all records in Trigger.new or Trigger.old
- Writing logic that can handle multiple records in a single execution.
So, Trigger can efficiently handle large-scale activities without performance issues.
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.isDelete: 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.
15. Can you control multiple Triggers for the same object?
The 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. It can lead to unpredictable interactions among the triggers.
Salesforce recommends that each object have only one Trigger to avoid potential complications. A Trigger Handler class allows you to regulate the sequence and execution of your logic easily.
Scenario-Based Salesforce Trigger Interview Questions
16. Write a trigger that updates a field in a parent Account record when a Contact record is updated.
In this case, I will use the after-update Trigger on the Contact object to update the parent Account. We can also loop through the Trigger.new records, check the changes, and then update the associated Account record.
trigger ContactTrigger on Contact (after update) {
List<Account> accList = new List<Account>();
for(Contact con : Trigger.new) {
if(con.AccountId != null) {
Account acc = new Account(
Id = con.AccountId,
Description = 'Contact record was updated'
);
accList.add(acc);
}
}
if(!accList.isEmpty()) {
update accList;
}
}
17. Write a trigger to stop the Account deletion record with “Active” status.
I would use a before-delete trigger and add logic to check if the status is “Active.” If it is, I’ll add an error to Trigger.old, preventing the deletion of those records.
trigger AccountTrigger on Account (before delete) {
for (Account acc : Trigger.old) {
// Checks if the custom Status field is set to 'Active'
if (acc.Status__c == 'Active') {
acc.addError('You cannot delete an Active account.');
}
}
}
18. Write a trigger that creates a follow-up task automatically when an Opportunity is marked as “Closed Won.”
In this scenario, firstly, I’d use an after-update trigger on Opportunity. I will check if the stage has been changed to “Closed Won,” if so, I will create a new task related to that Opportunity.
trigger OpportunityTrigger on Opportunity (After Update) {
List<Task> taskList = new List<Task>();
for (Opportunity opp : Trigger.new) {
if (opp.StageName == 'Closed Won' && Trigger.oldMap.get(opp.Id).StageName != 'Closed Won') {
Task t = new Task();
t.Subject = 'Follow-up on Closed Won Opportunity';
t.ActivityDate = System.today().addDays(7);
t.WhatId = opp.Id;
t.OwnerId = opp.OwnerId;
t.Status = 'Not Started';
taskList.add(t);
}
}
if (!taskList.isEmpty()) {
insert taskList;
}
}
19. How would you prevent a trigger from running multiple times on the same record?
I would implement a static boolean variable to ensure the Trigger runs only once during a specific transaction. This helps prevent recursion and infinite loops.
The Helper Class
public class AccountTriggerRunOnce {
// Boolean Variable -- initially it will be true.
public static Boolean isFirstRun = true;
}
-----Trigger ----
trigger AccountTrigger on Account (after update) {
if (Trigger.isAfter && Trigger.isUpdate) {
if (AccountTriggerRunOnce.isFirstRun) {
// set flag to false to prevent recursion
AccountTriggerRunOnce.isFirstRun = false;
List<Account> accountsToUpdate = new List<Account>();
for (Account acc : Trigger.new) {
if (acc.Description != 'Processed') {
Account updatedAcc = new Account(Id = acc.Id, Description = 'Processed');
accountsToUpdate.add(updatedAcc);
}
}
if (!accountsToUpdate.isEmpty()) {
update accountsToUpdate;
}
}
}
}
20. Can you write a trigger that checks if a Contact has a valid email format during insert/update?
Use a before insert and before update trigger to check if the email follows a valid format using a regular expression. If not, add an error to the Contact record to prevent saving invalid data.
trigger ContactEmailValidation on Contact (before insert, before update) {
for(Contact con : Trigger.new) {
// Check email contains @ and .
if(con.Email != null &&
(!con.Email.contains('@') || !con.Email.contains('.'))) {
con.addError('Enter a valid email address.');
}
}
}

Scenario: Prevent Duplicate Records
21. You need to prevent users from inserting duplicate Account records based on the Account Name. How would you write a trigger to enforce this rule?
PreventDuplicateAccounts on Account (before insert) {
Set<String> newAccountNames = new Set<String>();
for(Account acc : Trigger.new) {
newAccountNames.add(acc.Name);
}
List<Account> accList = [
SELECT Name
FROM Account
WHERE Name IN :newAccountNames
];
Set<String> accountNames = new Set<String>();
for(Account acc : accList) {
accountNames.add(acc.Name);
}
for(Account acc : Trigger.new) {
if(accountNames.contains(acc.Name)) {
acc.addError('An Account with this name already exists.');
}
}
}
This trigger runs before insert and checks for existing Accounts with the same name, preventing duplicates.
Scenario: Auto-Populate Contact’s Account Name
22. When a Contact is inserted, you need to automatically set its Account Name field based on the associated Account’s Name. How would you do this?
Salesforce automatically displays the account name on a contact record using the Account relationship field. Here, we don’t need to write code to set the Account Name field explicitly.
However, if there is a need to copy the Account Name into a Custom text field on a contact, a before-insert/update trigger that queries the Account via AccountID is needed.
Account acc = new Account(Name = 'Acme Corporation');
insert acc;
Contact newContact = new Contact();
newContact.FirstName = 'smith';
newContact.LastName = 'doe';
newContact.AccountId = acc.Id; // This establishes the relationship with the account and automatically populates the field.
insert newContact;
Scenario: Restrict Deletion of Key Accounts
23. Prevent users from deleting Accounts with Annual Revenue greater than $1M.
trigger PreventHighRevenueAccountDeletion on Account (before delete) {
for (Account acc : Trigger.old) {
if (acc.AnnualRevenue > 1000000) {
acc.addError('You cannot delete accounts with annual revenue over $1M.');
}
}
}
This trigger runs before deletion and blocks the deletion of high-revenue accounts.
Scenario: Create an Opportunity When an Account is Created
24. When an Account is inserted, automatically create a related Opportunity with a default Stage and Amount.
trigger CreateOpportunityOnAccountInsert on Account (after insert) {
List<Opportunity> oppList = new List<Opportunity>();
for(Account acc : Trigger.new) {
Opportunity opp = new Opportunity(
Name = acc.Name + ' - Initial Deal',
AccountId = acc.Id,
StageName = 'Prospecting',
CloseDate = Date.today().addMonths(1),
Amount = 5000
);
oppList.add(opp);
}
if(!oppList.isEmpty()) {
insert oppList;
}
}
This after-insert trigger creates an Opportunity with default values for StageName, CloseDate, and Amount whenever an Account is added.
Scenario: Update Parent Account’s Industry When a Child Account Changes
25. When a child Account’s Industry changes, update the parent Account’s Industry to match the child’s new Industry.
trigger UpdateParentAccountIndustry on Account (after update) {
Set<Id> parentAccountIds = new Set<Id>();
// Collect parent account IDs only when Industry changes
for(Account acc : Trigger.new) {
Account oldAcc = Trigger.oldMap.get(acc.Id);
if(acc.ParentId != null && acc.Industry != oldAcc.Industry) {
parentAccountIds.add(acc.ParentId);
}
}
if(parentAccountIds.isEmpty()) {
return;
}
// Query parent accounts
Map<Id, Account> parentAccounts = new Map<Id, Account>([
SELECT Id, Industry
FROM Account
WHERE Id IN :parentAccountIds
]);
List<Account> accountsToUpdate = new List<Account>();
// Update parent Industry to match child Industry
for(Account acc : Trigger.new) {
Account oldAcc = Trigger.oldMap.get(acc.Id);
if(acc.ParentId != null &&
acc.Industry != oldAcc.Industry &&
parentAccounts.containsKey(acc.ParentId)) {
Account parent = parentAccounts.get(acc.ParentId);
if(parent.Industry != acc.Industry) {
parent.Industry = acc.Industry;
accountsToUpdate.add(parent);
}
}
}
if(!accountsToUpdate.isEmpty()) {
update accountsToUpdate;
}
It ensures that when a child Account’s Industry field is updated, the parent Account’s Industry field is updated accordingly.
Scenario: Prevent Contact Deletion if Related Cases Exist
26. Prevent users from deleting a Contact if it has related Cases.
trigger PreventContactDeletionWithCases on Contact (before delete) {
Set<Id> contactIds = new Set<Id>();
for(Contact con : Trigger.old) {
contactIds.add(con.Id);
}
Map<Id, Integer> contactCaseCountMap = new Map<Id, Integer>();
for(AggregateResult ar : [
SELECT ContactId, COUNT(Id) caseCount
FROM Case
WHERE ContactId IN :contactIds
GROUP BY ContactId
]) {
contactCaseCountMap.put(
(Id)ar.get('ContactId'),
(Integer)ar.get('caseCount')
);
}
for(Contact con : Trigger.old) {
if(contactCaseCountMap.containsKey(con.Id) &&
contactCaseCountMap.get(con.Id) > 0) {
con.addError('Cannot delete Contact with related Cases.');
}
}
}
It ensures that Contacts with existing Cases cannot be deleted.
Scenario: Auto-Update Account Type Based on Number of Opportunities
27. When an Account has more than 5 closed won Opportunities, update its Type to “VIP Customer”.
trigger UpdateAccountTypeOnOpportunities on Opportunity (after insert, after update) {
Set<Id> accountIds = new Set<Id>();
for(Opportunity opp : Trigger.new) {
if(opp.StageName == 'Closed Won' && opp.AccountId != null) {
accountIds.add(opp.AccountId);
}
}
if(accountIds.isEmpty()) {
return;
}
Map<Id, Integer> oppCountMap = new Map<Id, Integer>();
for(AggregateResult ar : [
SELECT AccountId, COUNT(Id) oppCount
FROM Opportunity
WHERE StageName = 'Closed Won'
AND AccountId IN :accountIds
GROUP BY AccountId
]) {
oppCountMap.put(
(Id)ar.get('AccountId'),
(Integer)ar.get('oppCount')
);
}
List<Account> accountsToUpdate = new List<Account>();
for(Id accId : oppCountMap.keySet()) {
if(oppCountMap.get(accId) > 5) {
accountsToUpdate.add(
new Account(
Id = accId,
Type = 'VIP Customer'
)
);
}
}
if(!accountsToUpdate.isEmpty()) {
update accountsToUpdate;
}
}
It updates an Account’s Type to “VIP Customer” if it has more than 5 closed deals.
Scenario: Populate a Custom Field with Combined First and Last Name
28. Automatically populate a custom field Full_Name__c on Contact with the combination of FirstName and LastName.
On the Contact object, Salesforce already provides a standard Name field, which automatically stores and displays the full name by combining First Name and Last Name. It means users can already see the full name without any additional configuration or code.
So, in most cases, there is no need to create a custom field or write a trigger for this requirement.
Scenario: Log Every Update to a Custom Log Object
29. Whenever an Opportunity is updated, log the changes in a custom Opportunity_Log__c object.
trigger LogOpportunityUpdates on Opportunity (after update) {
List<Opportunity_Log__c> logs = new List<Opportunity_Log__c>();
for(Opportunity opp : Trigger.new) {
Opportunity oldOpp = Trigger.oldMap.get(opp.Id);
if(opp.StageName != oldOpp.StageName) {
logs.add(new Opportunity_Log__c(
OpportunityId__c = opp.Id,
Changed_Fields__c = 'StageName changed',
Updated_Date__c = System.now()
));
}
}
if(!logs.isEmpty()) {
insert logs;
}
}
It creates a log entry every time an Opportunity is updated.
Scenario: Prevent Changing Stage Back to “Prospecting”
30. Prevent users from changing an Opportunity’s stage back to “Prospecting” once it has been updated.
trigger RestrictStageReversion on Opportunity (before update) {
for (Opportunity opp : Trigger.new) {
Opportunity oldOpp = Trigger.oldMap.get(opp.Id);
if (oldOpp.StageName != 'Prospecting' && opp.StageName == 'Prospecting') {
opp.addError('Cannot revert to Prospecting stage.');
}
}
}
It prevents users from reverting an Opportunity to the Prospecting stage.
Winding Up
These interview questions can help you prepare for what to expect during the interview, whether you’re a beginner or an experienced professional seeking best practices.
Also, practicing the above-mentioned Salesforce trigger interview questions will help you pass your next Salesforce developer interview and land your dream job. If you want to get more insights about Salesforce development, you can join our Slack community.