Salesforce Dictionary - Free Salesforce GlossarySalesforce Dictionary
Salesforce Developer
hard

Walk me through writing a properly bulkified trigger handler.

The trigger framework pattern: trigger does nothing but delegate; logic lives in handler classes.

The trigger (one per object):

apex trigger AccountTrigger on Account (before insert, before update, after insert, after update, after delete) { new AccountTriggerHandler().run(); }

The handler base class:

apex public virtual class TriggerHandler { public void run() { if (Trigger.isBefore) { if (Trigger.isInsert) beforeInsert(); if (Trigger.isUpdate) beforeUpdate(); } else { // isAfter if (Trigger.isInsert) afterInsert(); if (Trigger.isUpdate) afterUpdate(); if (Trigger.isDelete) afterDelete(); } } public virtual void beforeInsert() {} public virtual void beforeUpdate() {} public virtual void afterInsert() {} public virtual void afterUpdate() {} public virtual void afterDelete() {} }

The Account-specific handler:

`apex public class AccountTriggerHandler extends TriggerHandler { public override void afterUpdate() { // Bulkified pattern: collect Ids, query once, process in memory, DML once. Set<Id> changedAccIds = new Set<Id>(); Map<Id, Account> oldMap = (Map<Id, Account>) Trigger.oldMap; for (Account a : (List<Account>) Trigger.new) { if (a.Phone != oldMap.get(a.Id).Phone) { changedAccIds.add(a.Id); } }

if (changedAccIds.isEmpty()) return;

List<Contact> toUpdate = new List<Contact>(); Map<Id, Account> newMap = (Map<Id, Account>) Trigger.newMap; for (Contact c : [SELECT Id, AccountId, Phone FROM Contact WHERE AccountId IN :changedAccIds]) { c.Phone = newMap.get(c.AccountId).Phone; toUpdate.add(c); }

if (!toUpdate.isEmpty()) update toUpdate; } } `

Why this is good:

  • One trigger per object — clear orchestration point.
  • Handler is testable — mock the trigger context in tests; no need to fire actual DML.
  • Bulkified — Sets, Maps, queries outside loops, single DML.
  • Conditional work — early return when nothing changed.
  • Type casts isolated — handler does the cast once.

Add as you grow:

  • A TriggerHandlerFactory that maps sObject name to handler class for dynamic dispatch.
  • A static recursion guard inside each handler to prevent re-entry.
  • Per-record action queues for cross-trigger collaboration (e.g., a handler updates state used by another handler in the same transaction).

Why this answer works

Senior. The complete pattern (trigger + base + concrete handler) is what differentiates a developer from someone who copy-pasted a tutorial. The recursion-guard mention is mature.

Follow-ups to expect

Related dictionary terms