Salesforce Dictionary - Free Salesforce GlossarySalesforce Dictionary
DictionaryAApex Triggers
DevelopmentIntermediate

Apex Triggers

An Apex trigger is a block of Apex code that runs automatically when records on a specific object are inserted, updated, deleted, or undeleted.

§ 01

Definition

An Apex trigger is a block of Apex code that runs automatically when records on a specific object are inserted, updated, deleted, or undeleted. It is the platform's primary hook for executing custom logic in response to data changes, and it has been the workhorse of programmatic Salesforce automation for fifteen years. Triggers fire at two timing points (before and after the database operation) for each of the four DML events, producing seven possible trigger contexts on each object.

A trigger is declared with the trigger keyword, a name, an object reference, and one or more event contexts: trigger AccountTrigger on Account (before insert, after insert, before update, after update). Inside the trigger, the Trigger.new, Trigger.old, Trigger.newMap, and Trigger.oldMap context variables expose the records being processed. Most production triggers delegate immediately to a handler class for the actual logic, keeping the trigger file as a thin dispatcher. This pattern (the Trigger Handler) is the foundation of every maintainable Apex codebase.

§ 02

How Apex triggers fit into the Salesforce save process

Before triggers, after triggers, and the save order

Before triggers fire after validation rules but before the record is saved to the database. They are the right place to modify fields on the record itself (Trigger.new is editable) because the changes save with the record automatically. After triggers fire once the record has been saved and given an ID. They are the right place to insert related records, send platform events, or do callouts that depend on the saved record ID. The full save order also includes workflow rules, Process Builder flows, record-triggered flows, escalation rules, and entitlement rules, each at specific points in the sequence.

Trigger.new, Trigger.old, and the context variables

Trigger context variables expose the records under processing. Trigger.new is the new state (available on insert, update, undelete). Trigger.old is the prior state (available on update, delete). Trigger.newMap and Trigger.oldMap provide ID-keyed maps for fast lookup. Trigger.isBefore and Trigger.isAfter (and the corresponding event flags like isInsert, isUpdate) let one trigger handle multiple events. The convention is to handle all events in one trigger per object, then dispatch by context to handler methods.

Bulkification: the single most important rule

Triggers fire on batches of up to 200 records by default. Code that does SOQL inside a loop, DML inside a loop, or assumes one record at a time will hit governor limits on the first real bulk operation. The bulkification pattern is: collect record IDs into a Set, run one SOQL query outside the loop, build a Map of related data, process each record using the map, and do one DML statement per object type at the end. Most "Apex performance" problems trace back to violations of this pattern.

Recursion control and trigger handler frameworks

Triggers can fire themselves recursively when their logic updates records that match their own criteria. A trigger that calculates a field on save updates the record, which triggers another save, which fires the trigger again. The standard guard is a static boolean in the handler class (Trigger_Once_Handler.alreadyRan) that the trigger checks at entry. More sophisticated patterns use a TriggerHandler framework that manages recursion, context dispatch, and event routing in a base class.

One trigger per object is the convention

Multiple triggers on the same object produce non-deterministic execution order because Salesforce does not guarantee which one runs first. The widely-adopted convention is one trigger per object, handling all events, dispatching to a handler class. This keeps the order of operations explicit and the codebase searchable. AccountTrigger.trigger handles every Account event and delegates to AccountTriggerHandler.cls. Diverging from this pattern produces hard-to-debug ordering issues.

Testing triggers requires bulk and edge cases

Trigger tests must cover the bulk case (200 records), the single-record case, and edge cases like deleted records, undeleted records, and records that match certain criteria. Use @testSetup to build common data once per class. Use Test.startTest and Test.stopTest to isolate governor limit counts in synchronous test methods. Cover both before and after contexts if both have logic. Coverage requirements apply but the more important goal is meaningful assertions that prove the trigger behaves correctly.

Triggers versus Flow and the modern split

Record-triggered Flow now handles most use cases that triggers used to be required for. The split today: use Flow for declarative automation that admins should be able to read and modify. Use Apex triggers when the logic exceeds Flow's expressiveness (complex string manipulation, intricate branching, callouts with retry logic, performance-critical bulk processing). Many production orgs use both, with Flow handling routine field updates and triggers handling the algorithmic work. The boundary is fuzzy but the rule of thumb is: start in Flow, switch to triggers when Flow feels fragile.

§ 03

How to write a maintainable Apex Trigger

Writing an Apex trigger is mechanically simple. Writing one that survives production data volumes, bulk operations, and three years of evolution is harder. Follow the conventions: one trigger per object, thin dispatcher with a handler class, bulkified logic, recursion control, and comprehensive tests. Skip any of these and the trigger becomes a debugging hotspot.

  1. Confirm Flow cannot do the job first

    Record-triggered Flow handles most save-event logic now. Use Apex triggers only when the logic exceeds Flow's expressiveness or performance envelope. This conversation with the architect saves weeks of rework later.

  2. Create the trigger file with all events declared

    One trigger per object, declaring all relevant events at once: trigger AccountTrigger on Account (before insert, after insert, before update, after update, before delete, after delete, after undelete). This is the convention and the entry point.

  3. Build the handler class with one method per context

    Separate file: AccountTriggerHandler.cls. Public methods for each event-context pair (handleBeforeInsert, handleAfterUpdate). The trigger delegates to these methods based on Trigger.isBefore, Trigger.isInsert, etc.

  4. Bulkify the logic with collections and maps

    Inside each handler method, collect record IDs, run SOQL outside loops, build maps of related data, process each Trigger.new record using the maps, then do DML outside the loop. This is the single most important discipline.

  5. Add recursion control via static boolean or framework

    Add a static boolean to the handler class (alreadyRan = false). Check it at entry to skip re-execution. For more complex needs, adopt a TriggerHandler framework like Hari Krishnan or Kevin Poorman patterns.

  6. Write the test class with bulk and edge cases

    Create AccountTriggerHandlerTest.cls. Use @testSetup for common data. Test the bulk case (insert 200 records). Test single-record cases. Test boundary conditions. Use System.assertEquals on the expected post-save state.

  7. Deploy with the test class and confirm coverage

    Deploy trigger and test class together. Confirm coverage above 75 percent. Run the tests in production after deployment to verify behavior matches sandbox. Set up a CI/CD pipeline that runs tests on every change.

  8. Monitor the trigger via Apex Jobs and exception logs

    Setup > Apex Jobs shows recent execution. Setup > Email Logs shows uncaught exceptions. Build a Custom Error_Log object and log caught exceptions to it for better debuggability than the default exception email.

Key options
Trigger Eventsremember

before insert, after insert, before update, after update, before delete, after delete, after undelete. Declare all events in one trigger per object.

Handler Classremember

Separate Apex class that holds the actual logic. The trigger should be a thin dispatcher that delegates to the handler.

Recursion Controlremember

Static boolean in the handler class or a TriggerHandler framework. Without it, save-cascade scenarios re-fire triggers infinitely.

Gotchas
  • Multiple triggers on the same object execute in non-deterministic order. Adopt the one-trigger-per-object convention from day one to avoid hard-to-debug ordering issues later.
  • Triggers fire on batches of up to 200 records. SOQL or DML inside a loop hits governor limits on the first bulk operation. Bulkification is mandatory, not optional.
  • Triggers can fire themselves recursively when their logic updates records they match. Use static booleans or a TriggerHandler framework to prevent infinite loops.
  • Before triggers can modify Trigger.new directly; the changes save automatically. After triggers cannot modify Trigger.new because the records are already saved.
  • Test coverage minimum is 75 percent across all Apex, including triggers. Write tests with meaningful assertions, not empty methods that just exercise lines for coverage credit.
§

Trust & references

Sources

Cross-checked against the following references.

Official documentation

Straight from the source - Salesforce's reference material on Apex Triggers.

Keep learning

Hands-on resources to go deeper on Apex Triggers.

Was this entry helpful?
Help us write better definitions. Quick reactions or detailed edit suggestions.

About the Author

Dipojjal Chakrabarti is a B2C Solution Architect with 29 Salesforce certifications and over 13 years in the Salesforce ecosystem. He runs salesforcedictionary.com to help admins, developers, architects, and cert/interview candidates sharpen their fundamentals. More about Dipojjal.

§

Test your knowledge

Q1. What is required before deploying Apex Triggers-related code to production?

Q2. Where would a developer typically work with Apex Triggers?

Q3. What is a Governor Limit in the context of Apex Triggers?

§

Discussion

Loading…

Loading discussion…