Bulk every time. A single insert someList is dramatically faster, uses one DML statement instead of N, and survives governor limits that single-record DML in a loop breaches on day one.
Why bulk wins on every axis
Governor limits
Salesforce caps you at 150 DML statements per transaction. A list of 200 records updated one at a time is 200 statements — already failed. One update myList is one statement, regardless of list size (up to 10,000 rows).
| Code shape | DML statements | DML rows | Outcome |
|---|---|---|---|
for (X x : list) update x; | 200 | 200 | Fine for 150 records, fails at 151 |
update list; | 1 | 200 | Plenty of headroom — limit is 150 statements, 10k rows |
Performance
Single-record DML still goes through the same multi-tenant locking, validation, and trigger pipeline as bulk DML — but you pay that overhead N times. A bulk save invokes triggers once, runs validation in batch, and amortises the round trip.
Transactional integrity
A bulk insert list is one atomic operation: either all records save or none do (unless you opt in to partial success). N single-record DMLs are N separate transactions to the user, but actually run within one Apex transaction — meaning if record 150 fails, records 1-149 are rolled back too, and you’ve still hit the limit.
Trigger semantics
Most Apex triggers are designed to be bulk-safe: Trigger.new is a list, queries use IN :Trigger.newMap.keySet(). Single-record DML inside a loop hands each record to the trigger one at a time, defeating bulkification at every level.
What “bulk DML” looks like
The right shape — build a collection, single DML at the end:
List<Account> toUpdate = new List<Account>();
for (Account a : Trigger.new) {
if (a.Annual_Revenue__c > 1_000_000) {
a.Tier__c = 'Enterprise';
toUpdate.add(a);
}
}
if (!toUpdate.isEmpty()) {
update toUpdate;
}
Compare to the antipattern:
for (Account a : Trigger.new) { // BAD
if (a.Annual_Revenue__c > 1_000_000) {
a.Tier__c = 'Enterprise';
update a; // one statement per record
}
}
“But I only have one record!”
Even then, prefer the list form — it’s the same number of characters and keeps the codebase consistent. Plus, “one record today, 200 tomorrow when someone triggers from a bulk save” is a real risk.
update new List<Account>{ a };
Or use the Database class for explicit control:
Database.update(new List<Account>{ a });
When you do need finer control: Database methods
Sometimes one record’s failure shouldn’t kill the rest. Use Database.insert(list, false) for partial-success semantics:
Database.SaveResult[] results = Database.update(list, false); // allOrNone = false
for (Integer i = 0; i < results.size(); i++) {
if (!results[i].isSuccess()) {
Database.Error err = results[i].getErrors()[0];
// log err.getStatusCode(), err.getMessage()
}
}
This is still bulk — one DML statement, one trigger pass, full bulkification — but with explicit per-record success reporting.
The 10,000-row hard cap
A single bulk DML statement processes up to 10,000 records. Need more? Use Batch Apex: each execute() chunk is a fresh transaction with its own 10,000-row budget.
What interviewers are really looking for
The answer is “always bulk” — full stop. The senior signal is why: 150-statement limit, 10,000-row limit, one trigger pass, atomic transaction, performance. Bonus: mention Database.update(list, false) for partial-success semantics — useful for data-loader-style imports where one bad row shouldn’t poison the batch. Mention Batch Apex as the answer when the list exceeds 10,000 records and you’ve covered the full progression.
Verified against: Apex Developer Guide — Bulk DML, Execution Governors and Limits. Last reviewed 2026-05-17.