In a CQRS front-end that shows an 'optimistic UI' after a user submits a command - for example, liking a post updates the like count instantly, before the read model confirms it - what has to happen if the command later fails validation, or the eventual read-model update disagrees with what the UI guessed?
answer
- predicted local state, tagged with a correlation id
- pending overlay reconciled on confirm/reject
- rollback plus error toast on failure
- reconcile against the eventual authoritative read model
- good for low-stakes actions, risky for payments
basics
~20 sThe UI has to notice the mismatch and fix itself - either roll back to the old value and show an error, or quietly swap in the real value once the read model catches up, so the screen never keeps showing a fact that turned out to be wrong.
solid answer
~50 sOptimistic UI renders the expected outcome of a command immediately, without waiting for the write to be acknowledged or the read model to reflect it, in order to hide the lag window from the user. This requires: a local, client-owned prediction of the state change; tracking that prediction as 'pending', tied to a command or correlation id; and reconciliation logic that, when the real result arrives - either a command rejection or the eventual authoritative read-model value - either confirms and discards the pending overlay if it matches, or rolls back to the last known-good state and surfaces an error if it doesn't. A typical implementation keeps 'pending mutations' in client-side state, triggers an explicit rollback plus a toast on command failure, and reconciles against the authoritative read model once it updates in the background.
go deeper
Should understand optimistic UI as 'show it now, confirm later' and know the prediction can turn out wrong and need correcting.
Should implement the reconcile/rollback logic for a real feature and know which action types are and aren't appropriate for it.
Should design correlation or versioning to avoid race conditions between overlapping optimistic updates, and set guidelines for which product flows may use it.
Should weigh optimistic UI against alternatives - blocking spinners, skeleton loading, CRDT-based real-time sync - at a product-architecture level and set the org's risk tolerance for false-positive UI.
## How the prediction is rendered Optimistic UI works by predicting the result of a command locally, on the client, and rendering that predicted state immediately — before the command has even been acknowledged by the server, let alone before an asynchronous read model has caught up. Concretely, this means the client keeps a small overlay of 'pending mutations' on top of its last known-good state, each tagged with a **correlation id** tying it back to the specific command that produced it. The user sees the guessed outcome right away: the like counter increments, the dragged card appears in its new column, the comment shows up in the thread. Later, one of two things happens: 1. the server confirms the command succeeded and the read model eventually reflects the same value the client predicted, in which case the pending overlay is simply discarded because reality matched the guess; 2. or the command is rejected, or the eventual authoritative value disagrees with the prediction, in which case the client must roll back to the last known-good state and typically surface an error or toast explaining what happened. ## Why the pattern exists This pattern exists purely to hide the lag window's latency from the user for actions where responsiveness matters more than absolute certainty. Waiting for a full round trip — command acknowledged, then read model updated, then UI refreshed — can take anywhere from tens of milliseconds to seconds, and for high-frequency, low-risk interactions like likes, reactions, or drag-and-drop reordering, that delay reads as sluggishness even though nothing is actually broken. By predicting the outcome and only correcting on the rare failure, the perceived responsiveness becomes near-instant for the common case. ## The trade-off The trade-off is complexity in exchange for perceived speed, and a real risk of user confusion when the prediction is wrong. - **Building correct reconciliation logic** — matching pending mutations to confirmations, handling partial failures, expiring stuck pending state if the server never responds — is materially more work than a simple loading spinner. - **Because optimistic UI shows an outcome before it's actually guaranteed**, any nontrivial failure rate translates into visible flicker: the user sees their action 'succeed' and then watches it get reverted, which for low-stakes actions is a minor annoyance but for high-stakes ones is a serious problem. - **This is precisely why optimistic UI is a poor fit for payments, checkout**, or any flow where a false 'success' has real consequence — showing a payment as accepted before the server has actually authorized it can mislead the user about something that matters. ## Failure modes Failure modes cluster around races and abandoned state. - If **two optimistic updates for the same field are in flight at once**, a late-arriving confirmation for the older mutation can overwrite a newer pending prediction unless updates are ordered by their correlation id or a local sequence number. - If **the server never responds at all** (a dropped connection, a timeout with no retry), a naively-implemented pending overlay can get stuck showing a prediction forever unless there's an explicit expiration or timeout that forces a rollback. - Because optimistic UI trusts the client's guess about what the server will do, **it can quietly mask authorization failures** — the UI shows success for an action the user actually lacked permission for, only reverting after the fact — which is a UX/trust bug worth calling out even though it isn't itself a security hole, since the server-side check still runs and still enforces the real rule. ## Where it shows up Well-known real-world examples make the pattern concrete. - **Twitter/X's 'like' button** is a textbook case: the heart fills in and the count increments the instant you tap it, well before any server round trip completes, and on the rare failure it silently reverts. - **Trello and similar kanban tools** apply the same idea to drag-and-drop card reordering — the card visually snaps to its new position immediately, and the client reconciles against the server's accepted position afterward, rolling back if the move is rejected (for example, because the target column was deleted concurrently). - **Tools with stronger real-time collaboration needs, like Google Docs or Figma**, go further and use operational transformation or CRDTs rather than simple optimistic-UI rollback, because they need to merge concurrent edits rather than just predict-and-correct a single user's own action — that's a related but more sophisticated technique built for a harder problem than optimistic UI alone solves.
- What kind of actions are a poor fit for optimistic UI?High-stakes or hard-to-reverse actions where a false 'success' has real cost - payments, inventory decrement at checkout, irreversible deletes, or anything gated by an authorization check that might fail server-side. Showing success before the server has actually committed misleads the user about a consequential outcome.
- How do you avoid two concurrent optimistic updates from the same user clobbering each other during reconciliation?Tag each optimistic update with its own correlation id or local mutation sequence number, and apply reconciliation in submission order, so a late-arriving confirmation for an older mutation doesn't overwrite a newer pending one. Some implementations diff against the last-predicted state rather than blindly overwriting it.
- How is optimistic UI different from optimistic concurrency control, like version-checked updates?Optimistic concurrency control is a server-side write-conflict-detection mechanism that rejects a write if a record's version changed since it was read. Optimistic UI is a client-side rendering strategy that assumes a command will succeed before confirmation arrives. They're often used together but solve different problems - one prevents lost updates, the other hides latency.
Like a restaurant expediter who calls out 'table 5, burger's coming!' the moment the order ticket prints, before the kitchen has actually cooked it - usually right, so the server can start setting up; but if the kitchen turns out to be out of buns, someone has to walk back to table 5 and correct the promise.
saying these in an interview costs you the question
- treats optimistic UI as just 'don't show a spinner' with no rollback path
- applies optimistic UI to payment or checkout flows without caveat
- no mention of reconciling the prediction against the eventual authoritative result
- confuses optimistic UI with optimistic locking/concurrency control