What Is an Apex Trigger?
An Apex trigger is a piece of code that executes automatically before or after specific data manipulation events occur in Salesforce — such as when a record is inserted, updated, deleted, or undeleted. Triggers are written in Apex, Salesforce's proprietary Java-like programming language.
While triggers are powerful, poorly written triggers are one of the most common sources of performance problems, governor limit errors, and bugs in Salesforce orgs. Following established best practices from the start will save you significant debugging time later.
Best Practice #1: One Trigger Per Object
The most foundational rule: create only one trigger per Salesforce object. Multiple triggers on the same object execute in an unpredictable order, making it nearly impossible to guarantee execution sequence and debug issues reliably.
Instead of creating multiple triggers, put all logic for a given object into a single trigger and delegate execution to a handler class.
Best Practice #2: Use the Trigger Handler Pattern
Keep your trigger file thin. All business logic should live in a separate Apex handler class. Here's a simple example:
// AccountTrigger.trigger
trigger AccountTrigger on Account (before insert, before update, after insert, after update) {
AccountTriggerHandler handler = new AccountTriggerHandler();
if (Trigger.isBefore) {
if (Trigger.isInsert) handler.onBeforeInsert(Trigger.new);
if (Trigger.isUpdate) handler.onBeforeUpdate(Trigger.new, Trigger.oldMap);
}
if (Trigger.isAfter) {
if (Trigger.isInsert) handler.onAfterInsert(Trigger.new);
if (Trigger.isUpdate) handler.onAfterUpdate(Trigger.new, Trigger.oldMap);
}
}
This pattern makes your code modular, testable, and much easier to maintain as requirements change.
Best Practice #3: Bulkify Your Code
Salesforce can process up to 200 records in a single trigger execution. Never write logic that assumes only one record is being processed. The classic anti-pattern is querying or performing DML inside a loop:
- Bad: Running a SOQL query inside a
forloop (hits the 100 SOQL query governor limit fast) - Good: Collect all record IDs into a
Set, run a single query outside the loop, then process results in aMap
Always design your trigger logic to handle bulk operations efficiently from day one, even if you only expect one record at a time — batch jobs and data imports will prove you wrong.
Best Practice #4: Avoid Hardcoding IDs
Salesforce record IDs differ between environments (sandbox vs. production). Hardcoding an ID like if (record.OwnerId == '0050x000001abc') will break when you deploy to a different org. Instead, query for the record dynamically using a unique external identifier, a custom metadata type, or a custom setting.
Best Practice #5: Use Context Variables Correctly
Apex triggers provide a set of context variables that tell you what's happening. Use them correctly:
| Variable | Description |
|---|---|
Trigger.new | List of new versions of records (insert/update) |
Trigger.old | List of old versions of records (update/delete) |
Trigger.newMap | Map of ID to new record (update/after insert) |
Trigger.oldMap | Map of ID to old record (update/delete) |
Trigger.isBefore | True if trigger is running before the save |
Trigger.isAfter | True if trigger is running after the save |
Best Practice #6: Write Meaningful Unit Tests
Salesforce requires at least 75% code coverage to deploy to production, but coverage alone is not the goal — meaningful assertions are. Your tests should:
- Test both positive and negative scenarios
- Use
System.assertEquals()to verify outcomes, not just that code runs without error - Test bulk behavior by inserting 200+ records in a single test
- Use
@testSetupmethods to share common test data efficiently
Best Practice #7: Respect Governor Limits
Salesforce enforces strict governor limits to ensure multi-tenant system stability. Key limits to watch:
- 100 SOQL queries per transaction
- 150 DML statements per transaction
- 50,000 records returned by SOQL queries
- 10,000 records processed by DML statements
Use the Apex Debug Log and Salesforce Inspector browser extension to monitor limit consumption during development.
Summary
Great Apex triggers are lean, bulkified, and testable. Keep business logic in handler classes, never put SOQL or DML inside loops, and treat governor limits as design constraints — not afterthoughts. Following these patterns will make your Salesforce org more reliable and your codebase far easier to hand off to future developers.