Bulkification Patterns
By the end of this lesson, you'll be able to:
- Apply the "collect, then act once" pattern to fix both SOQL-in-a-loop and DML-in-a-loop
- Recognize bulkification as a single, unified fix for both problems from this module
- Rewrite an unbulkified method to be safe at any data volume
Prerequisites: "The DML-in-a-Loop Problem"
The one fix for both problems
// Fixes SOQL-in-a-loop
Set<Id> accountIds = new Set<Id>();
for (Opportunity opp : opportunities) {
accountIds.add(opp.AccountId);
}
Map<Id, Account> accountsById = new Map<Id, Account>(
[SELECT Id, Name FROM Account WHERE Id IN :accountIds]
);
// Fixes DML-in-a-loop
List<Account> accountsToUpdate = new List<Account>();
for (Account acc : accounts) {
acc.Industry = 'Technology';
accountsToUpdate.add(acc);
}
update accountsToUpdate;
// Fixes SOQL-in-a-loop— the label for the first fix.Set<Id> accountIds = new Set<Id>();— collects, rather than queries, inside the loop.for (Opportunity opp : opportunities) {— loops over the opportunities.accountIds.add(opp.AccountId);— collects the id, no query yet.}— closes the loop.Map<Id, Account> accountsById = new Map<Id, Account>(— one query, outside the loop.[SELECT Id, Name FROM Account WHERE Id IN :accountIds]— fetches every needed Account at once.);— closes the constructor call.- (blank line)
// Fixes DML-in-a-loop— the label for the second fix.List<Account> accountsToUpdate = new List<Account>();— collects, rather than updates, inside the loop.for (Account acc : accounts) {— loops over the accounts.acc.Industry = 'Technology';— changes a field in memory.accountsToUpdate.add(acc);— collects it, no DML yet.}— closes the loop.update accountsToUpdate;— one DML statement, outside the loop.
Both fixes share the exact same shape: collect inside the loop, act once outside it — a Set/Map for reads (Module 16), a List for writes. This one pattern, applied consistently, is what "bulk-safe code" actually means in practice.
Rewriting an unbulkified method
// BEFORE: fails at scale
public void flagLargeOpportunities(List<Opportunity> opportunities) {
for (Opportunity opp : opportunities) {
if (opp.Amount > 100000) {
opp.Priority__c = 'High';
update opp; // DML in a loop
}
}
}
// AFTER: bulk-safe at any volume
public void flagLargeOpportunities(List<Opportunity> opportunities) {
List<Opportunity> toUpdate = new List<Opportunity>();
for (Opportunity opp : opportunities) {
if (opp.Amount > 100000) {
opp.Priority__c = 'High';
toUpdate.add(opp);
}
}
update toUpdate;
}
// BEFORE: fails at scale— the label for the flawed version.public void flagLargeOpportunities(List<Opportunity> opportunities) {— the method.for (Opportunity opp : opportunities) {— loops over the opportunities.if (opp.Amount > 100000) {— the business rule.opp.Priority__c = 'High';— applies it.update opp;— DML in a loop, once per matching record.}— closes theif.}— closes the loop.}— closes the method.- (blank line)
// AFTER: bulk-safe at any volume— the label for the fixed version.public void flagLargeOpportunities(List<Opportunity> opportunities) {— the same method signature.List<Opportunity> toUpdate = new List<Opportunity>();— collects instead of updating immediately.for (Opportunity opp : opportunities) {— the same loop.if (opp.Amount > 100000) {— the same rule.opp.Priority__c = 'High';— the same effect.toUpdate.add(opp);— collected, not yet saved.}— closes theif.}— closes the loop.update toUpdate;— oneupdatestatement, outside the loop.}— closes the method.
Same logic, same result — but the "after" version runs exactly one update statement regardless of whether opportunities has 5 records or 5,000, satisfying this module's core habit directly.
Exercise
Rewrite this method to be bulk-safe: collect matching Contacts into a list and update once, instead of updating inside the loop.
Show hint
Move update contact; outside the loop, collecting into a List<Contact> instead.
Bulkification Patterns Quiz
My Notes
Log in to keep private notes on this lesson.
Questions about this lesson
No questions yet — be the first to ask.
Log in to ask a question about this lesson.
Summary
Governor limits exist because Apex runs on shared, multi-tenant infrastructure. The single most important habit for staying within them is bulkifying your code: query and write in Lists, never inside a loop.