A single DML operates on one record at a time — insert account; where account is a single sObject. A bulk DML operates on a collection — insert accounts; where accounts is a List<Account>. They produce the same end result for one record, but in any code path that might handle more than one record, only the bulk form survives production.
Side-by-side
// Single DML
Account a = new Account(Name = 'Acme');
insert a;
// Bulk DML
List<Account> accounts = new List<Account>{
new Account(Name = 'Acme'),
new Account(Name = 'Beta'),
new Account(Name = 'Gamma')
};
insert accounts;
Both compile, both run. The difference shows up the moment you put a single DML inside a loop.
Why bulk wins
Apex’s synchronous governor limits cap a transaction at:
- 150 DML statements total
- 10,000 records affected by DML, total
A bulk insert of 200 records is one DML statement affecting 200 records. A loop doing 200 single inserts is 200 DML statements — and dies on the 151st. Every trigger and every async job in Salesforce can receive up to 200 records per invocation, so single-DML-in-loop is one of the fastest ways to break production.
// Anti-pattern — will fail at scale
for (Account a : accounts) {
a.Industry = 'Technology';
update a; // one statement per iteration
}
// Correct — one statement regardless of size
for (Account a : accounts) {
a.Industry = 'Technology';
}
update accounts; // one statement, up to 10,000 records
Comparison table
| Aspect | Single DML | Bulk DML |
|---|---|---|
| Statement count | One per record | One per list |
| Governor limit hit | At 151 records | At 10,001 records |
| Failure mode | One bad record kills the call | Database.update(list, false) allows partial success |
| Trigger fires | Fires once per record | Fires in batches of up to 200 |
| Code readability | Shorter for true single-record work | Clearer intent for set-based work |
| Production safety | Risky in any loop | Required for any bulk-aware context |
When single DML is genuinely fine
There are a few legitimate single-DML scenarios:
- A controller method that creates exactly one record from a Lightning component, with no possibility of receiving a list.
- A test setup method creating one parent record before bulk-creating children.
- An admin utility in anonymous Apex doing a one-off fix.
Even in these cases, defensive habits favour wrapping the record in a single-element list — costs nothing, scales to 200 if the requirement changes.
Bulkification beyond DML
The “bulk” mindset isn’t just about DML. The same logic applies to SOQL, callouts, and email sends — anything metered by the runtime. The trigger handler pattern that production code uses everywhere is:
- Loop through
Trigger.newonce to collect IDs into aSet. - Query once with
WHERE Id IN :ids. - Loop again to build a
List<sObject>of records to update. - One DML on that list.
That four-step pattern is what interviewers mean when they say “is this code bulk-safe?”
What interviewers are really looking for
The single-vs-bulk question is a bulkification proxy. The interviewer wants to know whether you’ve internalized that triggers and async jobs receive lists, not records. If you can explain why the 200-record batch size matters and quote the 150-DML governor limit by heart, you’ve answered the question they actually asked.
Verified against: Apex Developer Guide — Bulk DML Operations, Execution Governors and Limits. Last reviewed 2026-05-17 for Spring ‘26 release.