Apex Triggers: Best Practices and Common Pitfalls - Solution for Guru

Table of Contents
< All Topics
Print

Apex Triggers: Best Practices and Common Pitfalls

📝 Summary

This article provides essential best practices and highlights common pitfalls when working with Apex Triggers in Salesforce. Triggers are powerful tools for automating business logic, but they must be used with care to avoid performance and data integrity issues.


⚙️ What is an Apex Trigger?

Apex Triggers allow you to perform custom actions before or after events on records in Salesforce, such as insert, update, delete, or undelete.

Example:

trigger AccountTrigger on Account (before insert) {
for (Account acc : Trigger.new) {
acc.Name = acc.Name + ' - Validated';
}
}

✅ Best Practices

1. One Trigger Per Object

  • Avoid having multiple triggers on the same object. Instead, call a single handler class to keep logic organized.
  • Use a trigger framework for scalability.

trigger AccountTrigger on Account (before insert, before update) {
AccountTriggerHandler.handle(Trigger.new, Trigger.oldMap);
}

2. Keep Logic Out of Triggers

  • Triggers should delegate to Apex classes (handler classes or services).
  • This improves readability and makes unit testing easier.

3. Bulkify Your Code

  • Always assume multiple records are being processed (e.g., Trigger.new may contain 200 records).
  • Avoid SOQL and DML operations inside loops.

Good:

List contacts = [SELECT Id FROM Contact WHERE AccountId IN :accountIds];

🚫 Bad:

for (Account acc : Trigger.new) {
Contact c = [SELECT Id FROM Contact WHERE AccountId = :acc.Id];
}

4. Use Context Variables Wisely

  • Use Trigger.isInsert, Trigger.isUpdate, etc. to determine the trigger event.
  • Avoid duplicate logic across similar contexts.

5. Handle Recursion

  • Prevent logic from re-triggering itself using static variables.

public class TriggerControl {
public static Boolean hasRun = false;
}

6. Write Robust Test Classes

  • Ensure >75% code coverage.
  • Test all trigger events and edge cases (bulk insert, update, delete).
  • Use @testSetup to create reusable data.

7. Use Custom Settings/Metadata for Config

  • Use Custom Metadata to control trigger behavior (e.g., enable/disable logic per org or record type).

8. Fail Gracefully

  • Add appropriate error handling.
  • Use addError() for user-friendly validation messages.

⚠️ Common Pitfalls

PitfallDescriptionHow to Avoid
SOQL/DML in LoopsCauses governor limit exceptions.Move queries and operations outside loops.
Multiple TriggersHard to debug and maintain.Use one trigger per object with handler pattern.
Lack of TestingLeads to failed deployments.Write thorough unit tests.
Recursive TriggersInfinite loops or duplicate data changes.Use static flags to prevent re-entry.
Hardcoding IDsBreaks in different orgs.Use Custom Metadata, Labels, or dynamic queries.
Ignoring Bulk ProcessingFails for data imports or mass updates.Always test with batches of 200 records.

🔄 Recommended Trigger Frameworks

  • Salesforce Lightning Trigger Framework
  • Kevin O’Hara’s Apex Common Library
  • Custom-built frameworks based on business needs

These frameworks help structure logic, enforce consistency, and simplify debugging and testing.

🧪 Example: Scalable Trigger Handler Pattern

trigger ContactTrigger on Contact (before insert, before update) {
if (Trigger.isBefore && Trigger.isInsert) {
ContactTriggerHandler.beforeInsert(Trigger.new);
}
}

public class ContactTriggerHandler {
public static void beforeInsert(List contacts) {
for (Contact c : contacts) {
c.Description = ‘Created by trigger’;
}
}
}