Handle two optimistic changes and one failed response without erasing valid work.
An optimistic interface displays the expected effect before the server confirms it. This can reduce perceived delay, but it does not remove the need to resolve the actual outcome. The design needs an operation identity, a pending representation, and a rule for success, conflict, rejection, and unknown status.
A simple rollback stores the previous list and restores it on failure. That works only if no other relevant change occurs meanwhile. With overlapping mutations, restoring an old snapshot can erase a later successful update. A safer approach represents pending operations separately and derives the visible state from confirmed data plus those operations. On resolution, remove or replace the specific operation rather than restoring the entire application.
Choose optimism according to the cost of being wrong. Adding a local draft note can feel immediate because a rejected note can remain as an editable draft. Confirming a payment or a scarce reservation needs more careful wording because the user may act on the displayed result. A pending state can still feel responsive if it explains the next step and preserves the user's input.
Server identity matters when temporary client records become persistent records. A temporary note identifier should map to the returned note identifier once, without rendering two copies. If the response is lost after the server commits, a retry needs the same logical operation key. Otherwise the interface can show one note while the server stores two.
Worked example
A fictional task list has confirmed tasks A and B. The user creates C with operation M1 and D with operation M2. The display derives A, B, pending C, pending D. M2 succeeds first and returns permanent ID D9. Confirmed data becomes A, B, D9, while M1 remains pending. M1 then receives a permanent validation rejection. Remove only M1 from pending state and retain its text as an editable failed draft.
The final list contains A, B, and D9. A rollback to the original A/B snapshot would incorrectly remove D9. The user should receive a specific reason for C's rejection, not a generic message implying that both additions failed.
Derive the optimistic view from operations
A pending-operation log provides a precise alternative to whole-list rollback. The following teaching record is intentionally small.
code
1confirmed:2 A, B3pending:4 M1 -> create {temporaryId: C-temp, text: "C"}5 M2 -> create {temporaryId: D-temp, text: "D"}6visible:7 apply M1 and M2 to confirmed -> A, B, C-temp, D-temp
When M2 succeeds with server ID D9, add or reconcile D9 in confirmed state and remove M2 from pending. Keep a mapping from D-temp to D9 so the interface does not show both records. When M1 fails validation, remove only its visible pending effect and retain the text in an editable error state. The confirmed list and the remaining pending operations are independent of M1's failure.
A reducer can express these transitions without relying on request completion order. In a real query-cache library, the same principle may appear through mutation state and targeted cache updates. The library does not choose your business identity or conflict policy. Read its semantics and test overlapping operations rather than assuming a feature named optimistic handles every schedule.
Compare rejection with unknown outcome
Observation for M1
What is known
Appropriate view
Documented validation rejection
C was not accepted under that request
Editable failed draft
Success with permanent ID C8
C exists
Confirmed C8
Connection timeout after possible commit
Outcome unknown
Pending confirmation with M1 retained
Permission revoked before write
The protected write is denied
Retained draft plus access-aware message
A timeout is particularly important. If the client removes the optimistic item and then creates it again under a new operation key, the server may store two records. The client should use the API's documented recovery route. If the operation key is payload-bound, changing the text while recovering M1 is a different intent. Keep the old operation's unresolved result separate from a newly authorized edited submission.
A second case: optimistic deletion
Suppose the confirmed list contains A, B, and C. The user deletes B while another local action edits C. An optimistic view can hide B under delete operation M3 while preserving C's edit. If M3 is rejected, restore B's visibility without resetting C to an old version. The rollback is the inverse of M3's effect under the defined state model, not restoration of a whole historical snapshot.
Deletion can be more complicated when records have relationships. If deleting a project also removes access to child tasks, the interface must not invent a partial result that the server contract does not allow. The pending representation should make clear whether the project is awaiting deletion and whether child actions remain available. A failed deletion should restore a consistent permitted view.
Server-side deletion may also be irreversible. A button labelled undo needs a real contract, such as a delayed final delete or a restore operation. It cannot promise to reverse an external effect merely because the client retained an old object. Describe what is reversible and for how long.
Misconceptions to correct
The first misconception is that optimistic means pretending every request succeeded. It means showing a provisional result with a defined reconciliation path. Pending or uncertain wording can remain responsive while preserving truth.
The second misconception is that re-fetching after every failure always solves concurrent mutation state. A fresh server read can help, but it may still race with pending local operations. The view must decide how to combine confirmed data with unsent or unresolved intent.
A third failure is using array position as identity. When another item is inserted or removed, a rollback by index can target the wrong item. Stable operation and record identifiers make the change attributable even when ordering changes.
Extend the exercise
The user creates C with M1, then edits C's text before M1 resolves. A successful response returns C8 with the original text. State a safe result. Reconcile C-temp to C8, preserve the newer edit as unsaved intent, and submit it later under the appropriate version contract. Award one point for identity mapping, one for preserving the edit, and one for distinguishing creation success from edit persistence.
Exercise and solution
Change M1's rejection to a transport timeout after the server may have committed C. Should the client remove C as definitely rejected? No. Mark its outcome uncertain, preserve M1, and resolve through the API's idempotent retry or lookup contract. Award one point for unknown status, one for stable identity, and one for avoiding a duplicate creation with a fresh key.
Interview probe and wrap-up
How would you implement optimistic deletion? A strong answer considers a reversible hidden state, concurrent edits, authorization rejection, and what happens if the deleted item is already referenced elsewhere. Follow up with another user changing the record. A weak answer says to refresh the whole page whenever anything fails. Optimism is a temporary view of a specific operation, with explicit reconciliation rather than a promise that every request succeeds.
M2 succeeds before M1 fails. Which rollback preserves valid work?
ARemove both overlapping creations.BDiscard all server records and retain only local items.CRestore the list captured before M1.DRemove only M1's provisional effect while keeping M2's confirmed record.
A create timed out after a possible commit. Which action risks duplication?
AShow confirmation pending.BCreate the same intent with a fresh key before resolving the first.CPreserve the user's draft separately.DRetain the operation identity for lookup.
Temporary D-temp becomes permanent D9. What must reconciliation avoid?
ARendering both D-temp and D9 as separate tasks.BKeeping the server ID.CUpdating the operation's identity mapping.DRemoving M2 from pending after success.
ASorting before rollback makes every earlier index point to its original record.BA completed refetch preserves the old index-to-record mapping.COther insertions or removals can change which record occupies that index.DArray positions identify a logical operation more reliably than its operation ID.
An undo button follows an irreversible server deletion. What must exist for its promise to be valid?
AOnly a client copy of the old object.BA real restore or delayed-finalization contract with stated limits.CA retry of the original delete with a new transport trace.DA background refetch alone.
Can you derive a visible list from confirmed records plus pending operations, then reconcile a rejection, timeout, and temporary-ID replacement without erasing unrelated work? State the relevant identifiers, failure boundary, and evidence in your own words before selecting your confidence.