Create a child record only when a field changes
Triggers · Intermediate · 18 min · Runs in your Salesforce Developer org · Apex Path
Fire an `after update` trigger that logs one Task per Contact whose `Email` changed. Compare the new value to the old value from `Trigger.oldMap` so updates to other fields don't create a Task. Build the Tasks in a list and insert them once after the loop.
Lesson
Why this matters
Without a change check, an after update trigger that creates a child record fires on every update. Edit the Last Name, change nothing at all with a re-save, run a bulk integration update — you'd get a Task every time, flooding Compliance's queue. Trigger.oldMap lets you see the previous value and act only on real changes.
The smallest working example
trigger ContactEmailLog on Contact (after update) {
List<Task> logs = new List<Task>();
for (Contact row : Trigger.new) {
Contact prior = Trigger.oldMap.get(row.Id);
if (row.Email != prior.Email) {
logs.add(new Task(WhoId = row.Id, Subject = 'Email changed'));
}
}
if (!logs.isEmpty()) {
insert logs;
}
}
Name the parts
- **
Trigger.new** is the list of records being saved (new values). - **
Trigger.oldMap.get(row.Id)** is the matching record before this update. - **
row.Email != prior.Email** is true only when the value actually changed; a same-value save stays false. - The list collected inside the loop plus **one
insertafter the loop** keeps a 200-row bulk update to one DML statement.
A common wrong version
trigger ContactEmailLog on Contact (after update) {
for (Contact row : Trigger.new) {
insert new Task(WhoId = row.Id, Subject = 'Email changed');
}
}
Two problems. No comparison with oldMap, so every update creates a Task — even one that only touched Last Name. And DML inside the loop: 200 Contacts update → 200 DML statements → past the 150-DML limit.
The fix
Contact prior = Trigger.oldMap.get(row.Id);
if (row.Email != prior.Email) {
logs.add(new Task(WhoId = row.Id, Subject = 'Email changed'));
}
And move the DML out: if (!logs.isEmpty()) insert logs; after the loop.
Now you try
Fill in the trigger body in the starter. Keep the trigger name and the after update event — the hidden tests look for them exactly.
Learn more
The challenge
Create one Task when a Contact's Email changes. Do not create a Task when any other field changes or the Email is written back to the same value.
Examples:
- Change
Email→ one Task saved for that Contact - Change
LastNamebut keep Email → no Task - Bulk update 200 Contacts with different Emails → 200 Tasks in one insert
Requirements:
emailChangeCreatesTaskshould pass: an Email change creates one Task for that Contact.nonEmailChangeSkipsTaskshould pass: a LastName-only update creates no Task.sameEmailSkipsTaskshould pass: writing the same Email over itself is not a change.bulkUpdateCreatesOneTaskEachshould pass: a 200-record bulk update creates 200 Tasks in one insert.
Starter code
trigger AS_98365D23F261_ContactEmailLog on Contact (after update) {
}
Related drills
You write the Apex yourself. ApexSensei runs it and tells you what passed and what did not.