top of page

Apex Trigger Best Practices and Design Patterns in Salesforce

  • 1 day ago
  • 3 min read

Apex Triggers are one of the most powerful — and most misused — features in Salesforce development. Poorly written triggers cause governor limit exceptions, data corruption, and maintenance nightmares. This guide covers the definitive best practices and design patterns every Salesforce developer must follow when writing Apex Triggers.

What is an Apex Trigger?

An Apex Trigger is code that executes before or after specific DML events on a Salesforce object: insert, update, delete, and undelete. Triggers run in the context of a transaction and are subject to Salesforce governor limits. They are the go-to tool for complex automations that Flow Builder cannot handle.

Trigger Context Variables

Salesforce provides built-in context variables inside every trigger:

  • Trigger.new: List of new versions of the records (available in insert and update triggers)

  • Trigger.old: List of old versions of the records (available in update and delete triggers)

  • Trigger.newMap: Map of Id to new record (available in before update, after insert, after update)

  • Trigger.oldMap: Map of Id to old record (available in update and delete triggers)

  • Trigger.isBefore / Trigger.isAfter: Boolean flags to check execution context

  • Trigger.isInsert / Trigger.isUpdate / Trigger.isDelete / Trigger.isUndelete: Boolean flags for the DML event

Best Practice 1: One Trigger Per Object

Never create multiple triggers on the same object for the same DML event. Salesforce does not guarantee execution order between multiple triggers on the same object, which can lead to unpredictable behaviour. Always combine all logic into a single trigger per object, and delegate to a handler class.

Best Practice 2: Use the Trigger Handler Pattern

Never put business logic directly inside the trigger file. The trigger should only call methods on a handler class. This keeps your trigger lean, testable, and maintainable. The most widely used patterns are the Kevin O'Hara Trigger Framework and the Apex Enterprise Patterns (FFLIB).

A basic trigger handler pattern: The trigger file calls AccountTriggerHandler.handleBeforeInsert(Trigger.new) or AccountTriggerHandler.handleAfterUpdate(Trigger.newMap, Trigger.oldMap). The handler class contains all the actual logic.

Best Practice 3: Bulkify Your Triggers

Triggers must handle up to 200 records at a time (the maximum batch size for DML operations). Never write a trigger that assumes only one record is being processed. Avoid SOQL queries and DML operations inside loops — this is the most common cause of governor limit exceptions.

Pattern: Collect all required Ids into a Set before the loop, run a single SOQL query outside the loop, store results in a Map, and then iterate over Trigger.new using the Map for lookups.

Best Practice 4: Before vs After Triggers

  • Use Before triggers when you need to update fields on the record being saved (no DML needed — just modify Trigger.new directly). This is faster and uses fewer governor limits.

  • Use After triggers when you need to access the record's Id (not available in Before insert), update related records, or perform callouts.

Best Practice 5: Add a Trigger Bypass Mechanism

Always add a way to bypass trigger execution, typically via a Custom Setting or Custom Metadata flag. This is critical for data migrations where you need to load large volumes of data without firing business logic, and for debugging production issues. A bypass flag like TriggerSettings__c.AccountTriggerDisabled__c allows admins to toggle trigger execution without code deployment.

Best Practice 6: Write Comprehensive Unit Tests

Salesforce requires 75% code coverage for deployment, but aim for 90%+. More importantly, write meaningful assertions using System.assertEquals and System.assertNotEquals — not just empty tests that inflate coverage. Test bulk scenarios by inserting 200 records in your test methods to verify your trigger handles the maximum load.

Common Trigger Anti-Patterns to Avoid

  • SOQL inside a loop: Causes Too Many SOQL Queries exception with bulk data

  • DML inside a loop: Causes Too Many DML Statements exception

  • Recursive triggers: Trigger fires itself repeatedly. Use a static Boolean flag to prevent recursion.

  • Logic in the trigger file: Makes testing and maintenance difficult. Always use a handler class.

  • Hardcoded IDs: Never hardcode Record Type IDs or User IDs in triggers. Query them dynamically.

Conclusion

Writing clean, bulkified, and testable Apex Triggers is a hallmark of a strong Salesforce developer. Following these best practices ensures your org remains performant, maintainable, and scalable as data volumes grow. Stay tuned to SFDCPi for more Apex design patterns, code snippets, and real-world Salesforce developer guides.

Recent Posts

See All

Comments


Thanks for subscribing!

bottom of page