Lesson 9 of 26
Optimistic Updates and Rollback
Updating the UI before the server confirms a mutation to hide network latency, and the rollback and conflict-handling design it obligates you to build.
Every mutation — liking a post, checking off a to-do, sending a message — has an unavoidable round trip to a server, and that round trip takes real time. Optimistic updates are a UX bet: update the UI immediately, as if the mutation had already succeeded, and deal with the rare case where it didn't.
The mechanism
- Step 1
User takes an action
Taps 'like', for instance.
- Step 2
UI updates immediately
The like count and icon flip to their post-success state right away — no spinner, no wait.
- Step 3
Request fires to the server
The actual mutation is in flight, but the user has already moved on, believing it succeeded.
- Step 4
Server responds
On success, nothing further happens — the UI was already correct. On failure, the UI reverts and typically surfaces an error.
The entire value of this pattern rests on one empirical fact: most mutations succeed. If they didn't, optimism would just mean lying to the user most of the time. For actions like a like button, a to-do checkbox, or a comment post, success rates are high enough that the trade is clearly worth it — the interface feels instant, and the rare failure is an edge case rather than the common path.
What you're actually committing to build
Optimism isn't free — it's a specific, non-optional set of engineering obligations:
Rollback. When the server rejects or fails the mutation, the UI has to revert to its previous correct state. This means you need to have kept enough information to know what "revert" means — the prior like count, the prior checked state — not just "reload the page and hope."
User-visible failure handling. Silently reverting with no explanation is confusing — the user tapped something, saw it work, and now it's undone with no apparent cause. A toast, an inline error, or a retry affordance closes that loop.
Reconciling with the server's actual response, not just undoing your own guess. For a simple, single-user action this barely matters — revert to "before my tap" is basically correct. It gets genuinely harder once multiple mutations can be in flight concurrently (yours and someone else's, or two of your own actions racing) — the correct post-failure state isn't always "exactly what it was before I acted," it's "whatever the server now says is true," which usually means reconciling against the server's response or triggering a refetch rather than assuming your local rollback guess is precisely right.
Idempotency on the server side, ideally — if a flaky connection causes a retry, the server shouldn't double-apply the mutation (liking twice, charging twice). This is a backend concern, but a frontend optimistic-update strategy that doesn't account for possible retries is incomplete.
When optimism is the wrong bet
Not every mutation should be optimistic. High-stakes, hard-to-reverse actions — submitting a payment, deleting an account, sending a large transfer — are usually better with an honest pending state, because the cost of showing success and then having to walk it back is much higher than the cost of a brief, honest wait. The same applies to mutations with a meaningfully high failure rate, where the "bet" that most succeed doesn't hold — optimism there just means training users to distrust what your UI shows them.
The interview-strength framing is: optimistic UI is a latency-hiding trade that costs you a rollback path, and it's worth paying for frequent, low-stakes, high-success-rate actions, and usually not worth paying for rare, high-stakes, or failure-prone ones.
What to remember
- Optimistic updates hide network latency by updating the UI before the server confirms the mutation, betting that most mutations succeed.
- That bet obligates a real rollback path, plus a way to surface failure to the user — it is not free simplicity.
- Concurrent mutations make simple "undo my own change" rollback insufficient; reconciling against the server's actual response (or a refetch) is the more correct approach at scale.
- Reserve optimism for frequent, low-stakes, high-success-rate actions; prefer an honest pending state for rare, high-stakes, or failure-prone ones.
Check yourself
3 questions · pass 3/3 to unlock State Machines for Complex UI
1.What problem does an optimistic update solve that a normal 'wait for the response, then update the UI' flow doesn't?
2.What does an optimistic update commit you to building, that a standard wait-then-update flow doesn't need?
3.A 'like' button flips to liked immediately on tap, then the request to the server fails due to a network drop. Two users tap 'like' on the same post around the same time from different devices. What's the added complexity beyond simple rollback?
3 left to answer