Apex triggers are a powerful feature in Salesforce that allow developers to execute custom code before or after specific database operations. Mastering triggers is essential for building efficient, reliable, and scalable applications on the Salesforce platform. In this blog post, we’ll delve into best practices for writing Apex triggers and highlight common pitfalls to avoid.
Apex triggers enable you to perform custom actions before or after events such as insert, update, delete, and undelete. They are particularly useful for:
Having a single trigger per object simplifies maintenance and enhances readability.
trigger AccountTrigger on Account (before insert, before update, after insert, after update) {AccountTriggerHandler.handle(Trigger.new, Trigger.oldMap);}
In this example, all logic is delegated to a handler class, promoting separation of concerns.
Move complex logic to Apex classes. This makes your code more modular and easier to test.
public class AccountTriggerHandler {public static void handle(List<Account> newList, Map<Id, Account> oldMap) {if (Trigger.isBefore) {if (Trigger.isInsert) {beforeInsert(newList);} else if (Trigger.isUpdate) {beforeUpdate(newList, oldMap);}} else if (Trigger.isAfter) {if (Trigger.isInsert) {afterInsert(newList);}}}private static void beforeInsert(List<Account> newList) {// Implement logic for before insert}private static void beforeUpdate(List<Account> newList, Map<Id, Account> oldMap) {// Implement logic for before update}private static void afterInsert(List<Account> newList) {// Implement logic for after insert}}
Always design your triggers to handle multiple records.
public static void beforeUpdate(List<Account> newList, Map<Id, Account> oldMap) {List<Account> accountsToUpdate = new List<Account>();for (Account acc : newList) {Account oldAcc = oldMap.get(acc.Id);if (acc.Industry != oldAcc.Industry) {acc.Description = 'Industry has changed.';accountsToUpdate.add(acc);}}if (!accountsToUpdate.isEmpty()) {update accountsToUpdate;}}
Perform all queries and DML operations outside of loops to avoid governor limits.
// Bad Practicefor (Account acc : Trigger.new) {List<Contact> contacts = [SELECT Id FROM Contact WHERE AccountId = :acc.Id];// Process contacts}// Best PracticeSet<Id> accountIds = new Set<Id>();for (Account acc : Trigger.new) {accountIds.add(acc.Id);}List<Contact> contacts = [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds];// Process contacts outside the loop
Utilize Sets, Maps, and Lists to manage data efficiently.
Map<Id, Account> accountMap = new Map<Id, Account>([SELECT Id, Name FROM Account WHERE Id IN :accountIds]);
Use try-catch
blocks to handle exceptions gracefully.
try {// Your code here} catch (Exception e) {System.debug('An error occurred: ' + e.getMessage());}
Avoid using hardcoded IDs or values, as they can cause issues when deploying to different environments.
// AvoidString recordTypeId = '01230000000ABC';// Use Describe methods insteadString recordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByName().get('Business').getRecordTypeId();
Salesforce enforces limits to ensure efficient resource use. Always design with these limits in mind.
Leverage context variables like Trigger.isInsert
, Trigger.isUpdate
, Trigger.new
, and Trigger.oldMap
to control your logic.
if (Trigger.isUpdate) {// Update-specific logic}
Unintentionally updating records within a trigger can lead to infinite loops.
Solution: Use static variables to prevent recursion.
public class TriggerHelper {public static Boolean isFirstRun = true;}trigger ContactTrigger on Contact (before update) {if (TriggerHelper.isFirstRun) {TriggerHelper.isFirstRun = false;// Your logic here}}
Ensure your triggers are covered by unit tests with adequate assertions.
@isTestprivate class AccountTriggerTest {@isTest static void testAccountTrigger() {Account acc = new Account(Name = 'Test Account', Industry = 'Technology');insert acc;acc.Industry = 'Finance';update acc;Account updatedAcc = [SELECT Description FROM Account WHERE Id = :acc.Id];System.assertEquals('Industry has changed.', updatedAcc.Description);}}
Mastering Apex triggers involves understanding best practices and being aware of common pitfalls. By following the guidelines outlined above, you can write efficient, maintainable, and scalable triggers that enhance your Salesforce applications.
Key Takeaways:
By adhering to these best practices, you’ll be well on your way to becoming proficient in Apex trigger development.
Learn more about Apex Triggers
Happy coding on your Salesforce journey!
Quick Links
Legal Stuff