The FOR UPDATE clause on a SOQL query — the locking statement — is how you prevent lost updates in a multi-user system. Without it, two concurrent transactions can both read the same record, both compute new values, and both write — the second write silently overwrites the first.
The race that FOR UPDATE prevents
Transaction A Transaction B
--------------- ---------------
SELECT Balance: 100
SELECT Balance: 100
Balance := 100 + 50 = 150
Balance := 100 - 30 = 70
UPDATE to 150
UPDATE to 70 ← A's update silently lost
After both transactions commit, the balance is 70. The +50 deposit from A is gone.
With FOR UPDATE
Account a = [SELECT Id, Balance__c FROM Account WHERE Id = :acctId FOR UPDATE];
a.Balance__c += amount;
update a;
Now the second transaction blocks at its SELECT ... FOR UPDATE until the first one commits or rolls back. When B is finally allowed to read, it sees the post-A balance and computes the correct result.
Where the lock is held
A FOR UPDATE lock is taken on the specific row(s) returned by the query. The lock is released when the transaction commits or rolls back. It is NOT held across:
- HTTP callouts (the platform releases the lock before the callout, takes it back after — risky)
- Async chains (each async transaction is fresh)
Database.rollback()to a savepoint — but the transaction still holds it until tx end
The cost of locking
Locks aren’t free. They:
- Reduce concurrency — other transactions trying to touch the same row queue up behind yours.
- Risk deadlocks — two transactions each waiting for a row the other holds. Salesforce’s deadlock detector eventually kills one, but the user sees an error.
- Cost CPU/throughput on the database tier — measured in your shared tenant budget.
Rule of thumb: lock only when you actually have a read-modify-write that must be atomic.
When FOR UPDATE is the right tool
- Inventory or balance updates that depend on the current value.
- Counter increments (
Updated_Count__c += 1). - “First user to claim this record wins” pickers.
- Sequence number generation (you read the next number, increment, write back).
When it isn’t
- Blind writes —
update new Account(Id = id, Status__c = 'Won')doesn’t need a lock; it overwrites whatever was there. - Read-only queries — no DML coming, no lock needed.
- Bulk imports where conflicts are rare — the cost of locking exceeds the cost of occasional conflicts that you handle by retrying.
What the platform already locks for you
Even without FOR UPDATE, Salesforce implicitly locks records during DML. Any update myRecords takes row-level locks until the transaction ends. So:
- Two
update Accounttransactions touching the same record will serialize anyway. - The risk
FOR UPDATEprevents is specifically the read-then-write case where the read isn’t itself a DML.
Combining with related records
FOR UPDATE only takes the lock on the directly queried rows. Child records and parent records are NOT auto-locked. If your logic depends on parent + child consistency, you query both with FOR UPDATE:
Account a = [SELECT Id, Balance__c FROM Account WHERE Id = :acctId FOR UPDATE];
List<Transaction__c> txs = [
SELECT Id, Amount__c FROM Transaction__c
WHERE Account__c = :acctId AND Status__c = 'Pending'
FOR UPDATE
];
// ... compute new balance from txs, update a
Failure mode: UNABLE_TO_LOCK_ROW
If the lock isn’t acquired within the timeout (about 10 seconds by default), the runtime throws:
UNABLE_TO_LOCK_ROW: unable to obtain exclusive access to this record
In production, this is the most common locking failure. Fixes:
- Shorten the work inside the lock (do read, then DML, then release — fast).
- Don’t
FOR UPDATEthousands of rows when only a few are contended. - Retry the transaction (most ORMs and the platform do this automatically up to a point).
What interviewers are really looking for
The basic answer is “prevents lost updates.” The senior answer adds: (1) FOR UPDATE is for read-modify-write atomicity, (2) DML already locks implicitly — FOR UPDATE only matters before the DML, (3) locks don’t survive callouts cleanly, (4) UNABLE_TO_LOCK_ROW is the production failure to recognise, (5) over-locking causes deadlocks and reduced concurrency — only lock what you must.
Verified against: Apex Developer Guide — Locking Statements, SOQL
FOR UPDATE. Last reviewed 2026-05-17.