When you write Apex triggers, the magic comes from the built-in Trigger context variables. These are the values Salesforce provides automatically, and they tell your code what is happening.
If you already know how to loop over Trigger.new, the next step is understanding how to use
Trigger.isInsert, Trigger.isUpdate, Trigger.newMap, and the rest.
They let you write a single trigger that handles multiple events safely. Without them, you would not know whether the code is inserting, updating, or deleting records.
trigger OpportunityTrigger on Opportunity (before insert, before update, after update, after delete) {
if (Trigger.isBefore) {
handleBefore(Trigger.new);
}
if (Trigger.isAfter) {
handleAfter(Trigger.new, Trigger.oldMap);
}
}
Trigger.new for new/changed values
In before insert and before update, Trigger.new contains the records you can modify.
That is the place to add validation, normalization, and default values.
for (Opportunity opp : Trigger.new) {
if (opp.CloseDate == null) {
opp.addError('Close Date is required');
}
}
Trigger.old to compare past values
When updating or deleting records, Trigger.old gives you the original values before the change.
This is important for change detection and audit-like logic.
for (Opportunity opp : Trigger.new) {
Opportunity oldOpp = Trigger.oldMap.get(opp.Id);
if (oldOpp.StageName != opp.StageName) {
// Stage changed
}
}
trigger OpportunityStageTrigger on Opportunity (before insert, before update) {
if (Trigger.isInsert) {
for (Opportunity opp : Trigger.new) {
opp.StageName = 'Prospecting';
}
}
if (Trigger.isUpdate) {
for (Opportunity opp : Trigger.new) {
Opportunity oldOpp = Trigger.oldMap.get(opp.Id);
if (oldOpp.StageName != opp.StageName) {
// Do something when stage changes
}
}
}
}
Always write trigger logic with bulk records in mind. Trigger.new may contain many records,
so never assume only one record is present.
Use methods like handleBefore or handleAfter to keep the trigger body clean and readable.