Apex Triggers and Context Variables
Apex Triggers are specialized pieces of Apex code that execute before or after specific Data Manipulation Language (DML) events occur on Salesforce records, such as insert, update, delete, merge, upsert, and undelete operations. They allow developers to perform custom actions and implement complex … Apex Triggers are specialized pieces of Apex code that execute before or after specific Data Manipulation Language (DML) events occur on Salesforce records, such as insert, update, delete, merge, upsert, and undelete operations. They allow developers to perform custom actions and implement complex business logic that cannot be achieved through declarative tools alone. There are two types of triggers: **Before Triggers** are used to validate or modify field values before records are saved to the database, while **After Triggers** are used to access system-set field values (like record IDs) and perform operations on related records after the record has been committed. Triggers are defined using the syntax: `trigger TriggerName on ObjectName (trigger_events) { // code }` **Context Variables** are system-defined variables available within the trigger context that provide critical information about the runtime environment: - **Trigger.new**: Returns a list of the new versions of records (available in insert, update, and undelete triggers). - **Trigger.old**: Returns a list of the old versions of records (available in update and delete triggers). - **Trigger.newMap**: A map of IDs to new versions of records (available in before update, after insert, after update, and after undelete). - **Trigger.oldMap**: A map of IDs to old versions of records (available in update and delete triggers). - **Trigger.isBefore / Trigger.isAfter**: Boolean values indicating whether the trigger fired before or after the record was saved. - **Trigger.isInsert / Trigger.isUpdate / Trigger.isDelete / Trigger.isUndelete**: Booleans indicating the DML operation that fired the trigger. - **Trigger.size**: The total number of records in the trigger invocation. - **Trigger.operationType**: Returns a System.TriggerOperation enum value corresponding to the current operation. Best practices include writing bulkified triggers, using one trigger per object, and delegating logic to handler classes to maintain clean, maintainable code and avoid governor limit issues.
Apex Triggers and Context Variables – Salesforce Platform Developer 1 Exam Guide
Why Apex Triggers and Context Variables Matter
Apex Triggers are one of the most foundational topics on the Salesforce Platform Developer 1 (PD1) exam. They are the primary mechanism for executing custom Apex code in response to DML operations (such as insert, update, delete, and undelete) on Salesforce records. Understanding triggers and their context variables is essential not only for passing the exam but also for building robust, scalable Salesforce applications. The Process Automation and Logic section of the exam accounts for a significant portion of the total score, and triggers are heavily represented in this domain.
What Are Apex Triggers?
An Apex Trigger is a piece of Apex code that executes before or after specific data manipulation language (DML) events occur on a Salesforce object. Triggers allow developers to perform custom actions such as validation, field updates on related records, integration callouts (via asynchronous methods), and complex business logic that cannot be achieved through declarative tools alone.
There are two main types of triggers:
1. Before Triggers – These execute before a record is saved to the database. They are typically used to validate or modify field values on the record being inserted or updated. Because the record has not yet been committed to the database, you do not need an explicit DML statement to save changes to the triggering record itself.
2. After Triggers – These execute after a record has been saved to the database. At this point, the record has an ID (in the case of insert) and all system fields are populated. After triggers are commonly used for operations that require the record ID, such as creating related records or making changes to other objects. Records in after triggers are read-only, so you cannot modify the triggering records directly.
Trigger Syntax
A trigger is defined on a specific sObject and specifies one or more events:
trigger TriggerName on ObjectName (before insert, before update, after insert, after update, before delete, after delete, after undelete) {
// Trigger logic here
}
You can include any combination of the seven trigger events in a single trigger definition.
What Are Trigger Context Variables?
Trigger context variables are implicit variables provided by the Apex runtime that give you information about the trigger invocation. They are accessed via the Trigger class and are essential for writing logic that behaves correctly depending on the operation being performed.
Here are the key context variables you must know:
Trigger.new – A list of the new versions of the sObject records. Available in insert, update, and undelete triggers. In before triggers, these records can be modified directly without a DML call.
Trigger.old – A list of the old versions of the sObject records. Available in update and delete triggers. These are always read-only.
Trigger.newMap – A map of IDs to the new versions of the sObject records. Available in before update, after insert, after update, and after undelete triggers. Note: In before insert, records do not yet have IDs, so Trigger.newMap is not available in before insert context.
Trigger.oldMap – A map of IDs to the old versions of the sObject records. Available in update and delete triggers.
Trigger.isExecuting – Returns true if the current context is a trigger (not a Visualforce page, web service, or anonymous block).
Trigger.isBefore – Returns true if the trigger fired before the record was saved.
Trigger.isAfter – Returns true if the trigger fired after the record was saved.
Trigger.isInsert – Returns true if the trigger fired due to an insert operation.
Trigger.isUpdate – Returns true if the trigger fired due to an update operation.
Trigger.isDelete – Returns true if the trigger fired due to a delete operation.
Trigger.isUndelete – Returns true if the trigger fired due to an undelete operation (restoring from recycle bin).
Trigger.size – Returns the total number of records in the trigger invocation, both old and new combined.
Trigger.operationType – Returns a System.TriggerOperation enum value corresponding to the current operation (e.g., BEFORE_INSERT, AFTER_UPDATE, etc.).
How Triggers Work – The Execution Flow
Understanding the order of execution is critical:
1. The original record is loaded from the database (or initialized for inserts).
2. The new field values from the request overwrite the old values.
3. System validation rules run (required fields, field formats).
4. Before triggers execute.
5. Custom validation rules run.
6. The record is saved to the database (but not committed).
7. After triggers execute.
8. Assignment rules, auto-response rules, and workflow rules execute.
9. Workflow field updates fire, and if they occur, before and after triggers fire again.
10. Escalation rules execute.
11. Process Builder and Flows execute.
12. DML operations are committed to the database.
This order of execution is frequently tested and is critical for understanding when data is available and when records can be modified.
Key Concepts for the Exam
1. Bulkification
Triggers must be written to handle multiple records at once. Salesforce can pass up to 200 records in a single trigger invocation (e.g., via Data Loader, batch Apex, or bulk API). Always iterate over Trigger.new or Trigger.old instead of assuming a single record. Never place SOQL queries or DML statements inside a for loop.
2. Trigger.new vs Trigger.old for Detecting Changes
In update triggers, you can compare Trigger.new and Trigger.old (or use Trigger.newMap and Trigger.oldMap) to determine if a specific field value has changed. For example:
if (newRecord.Status__c != Trigger.oldMap.get(newRecord.Id).Status__c) { ... }
3. Before Insert – No ID Available
Records in a before insert context do not yet have an ID. Therefore, Trigger.newMap is not available in before insert triggers. This is a commonly tested detail.
4. Read-Only Records in After Triggers
In after triggers, the records in Trigger.new are read-only. If you attempt to modify them, you will receive a runtime error. To update the same records in an after trigger context, you must query them and perform a separate DML update (though this is generally discouraged due to recursion risks).
5. Trigger.old Availability
Trigger.old is only available in update and delete triggers. It is not available in insert or undelete triggers because there is no previous version of the record.
6. Using addError() for Validation
You can call .addError('message') on records in Trigger.new (before triggers) or Trigger.old (before delete triggers) to prevent the DML operation and display a custom error message. This is a key method for implementing custom validation logic in triggers.
7. One Trigger Per Object Best Practice
Salesforce best practices recommend having only one trigger per object and delegating logic to handler classes. This is known as the Trigger Handler Pattern or Trigger Framework. While the exam may not test specific frameworks, it does test the concept that having multiple triggers on the same object leads to unpredictable execution order.
8. Recursive Triggers
If a trigger performs a DML operation that causes the same or another trigger to fire, recursion can occur. Developers typically use a static Boolean variable in a helper class to prevent a trigger from executing more than once per transaction.
9. Context Variable Availability Summary
- before insert: Trigger.new (writable), Trigger.old (NOT available), Trigger.newMap (NOT available), Trigger.oldMap (NOT available)
- after insert: Trigger.new (read-only), Trigger.old (NOT available), Trigger.newMap (available), Trigger.oldMap (NOT available)
- before update: Trigger.new (writable), Trigger.old (read-only), Trigger.newMap (available), Trigger.oldMap (available)
- after update: Trigger.new (read-only), Trigger.old (read-only), Trigger.newMap (available), Trigger.oldMap (available)
- before delete: Trigger.new (NOT available), Trigger.old (read-only), Trigger.newMap (NOT available), Trigger.oldMap (available)
- after delete: Trigger.new (NOT available), Trigger.old (read-only), Trigger.newMap (NOT available), Trigger.oldMap (available)
- after undelete: Trigger.new (read-only), Trigger.old (NOT available), Trigger.newMap (available), Trigger.oldMap (NOT available)
Exam Tips: Answering Questions on Apex Triggers and Context Variables
Tip 1: Memorize the Context Variable Availability Table
Many questions are specifically designed to test whether you know which context variables are available in which trigger events. Know that Trigger.newMap is NOT available in before insert, and that Trigger.old is NOT available in insert or undelete contexts. Create a mental grid of events vs. variables.
Tip 2: Know When Records Are Writable vs. Read-Only
A common exam question pattern presents code that modifies a record in an after trigger. Recognize that this will cause a runtime error. If a question asks how to set a field value on the triggering record without an additional DML statement, the answer is always to use a before trigger.
Tip 3: Look for Bulkification Issues
If you see a SOQL query or DML statement inside a for loop iterating over Trigger.new, that code will hit governor limits when processing bulk data. Many exam questions present code snippets and ask you to identify the problem – SOQL or DML inside loops is one of the most common issues tested.
Tip 4: Understand Before vs. After Use Cases
Before triggers: modifying fields on the current record, custom validation. After triggers: creating or updating related records, sending emails, enqueuing asynchronous jobs. If a question asks about updating a child record after a parent is inserted, the answer involves an after insert trigger (because the parent ID is needed).
Tip 5: Be Careful with Delete Triggers
In delete triggers, Trigger.new is NOT available. The records being deleted are in Trigger.old. If you see code using Trigger.new in a before delete or after delete context, it will fail. Use Trigger.old and Trigger.oldMap instead.
Tip 6: Recognize the addError() Pattern
When a question asks about preventing a record from being saved and displaying an error to the user within a trigger, the correct approach is to call addError() on the record. In before insert/update triggers, call it on records in Trigger.new. In before delete triggers, call it on records in Trigger.old.
Tip 7: Order of Execution Questions
Some questions test whether you understand that before triggers fire before custom validation rules, and that workflow field updates can cause triggers to fire again. If a question involves complex interaction between triggers, validation rules, and workflows, trace through the order of execution step by step.
Tip 8: Static Variables to Prevent Recursion
If a question describes a scenario where a trigger is executing multiple times and asks for the best solution, look for an answer that involves a static Boolean variable in a separate class to control execution.
Tip 9: One Trigger Per Object
If a question asks about best practices for trigger design, remember that the recommended pattern is one trigger per object, with logic delegated to handler classes. The order in which multiple triggers on the same object fire is not guaranteed.
Tip 10: Trigger.operationType and Switch Statements
Salesforce introduced the Trigger.operationType enum which works well with switch statements. A modern best practice pattern uses switch on Trigger.operationType to route logic. Know the enum values: BEFORE_INSERT, BEFORE_UPDATE, BEFORE_DELETE, AFTER_INSERT, AFTER_UPDATE, AFTER_DELETE, AFTER_UNDELETE.
Tip 11: Eliminate Wrong Answers
When you encounter a question with a code snippet, first identify the trigger event (before/after and insert/update/delete/undelete). Then check which context variables the code uses. If the code uses a variable that is not available in that context, you can immediately eliminate that answer or identify the error. This is the fastest way to solve many trigger-related exam questions.
Summary
Apex Triggers and Context Variables are a cornerstone of the Platform Developer 1 exam. To succeed, you must understand the difference between before and after triggers, know exactly which context variables are available in each event, write bulkified trigger code, and follow best practices like the one-trigger-per-object pattern. Practice writing triggers in a developer org, experiment with each context variable, and review the order of execution thoroughly. Mastery of this topic will not only help you pass the exam but will also make you a more effective Salesforce developer.
🎓 Unlock Premium Access
Salesforce Certified Platform Developer I + ALL Certifications
- 🎓 Access to ALL Certifications: Study for any certification on our platform with one subscription
- 2750 Superior-grade Salesforce Certified Platform Developer I practice questions
- Unlimited practice tests across all certifications
- Detailed explanations for every question
- PD1: 5 full exams plus all other certification exams
- 100% Satisfaction Guaranteed: Full refund if unsatisfied
- Risk-Free: 7-day free trial with all premium features!