awesome-everything RU
↑ Back to the climb

Queues, Streams, Eventing

Eventual-consistency UX: make an async backend feel instant

Crux Hands-on project — build a UI over a deliberately laggy async backend and make it feel responsive: optimistic UI with rollback, honest pending states, idempotent retries, and legible conflict resolution.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at senior altitude — in orbit
◷ 240 min

Reading about the consistency window is not the same as designing a UI that survives it. Build a small app over a backend that deliberately returns 202 and writes ~700ms later, then apply every tool from the unit until the experience feels instant and honest — with the failure modes provably handled.

Goal

Turn the unit’s mental model into a working frontend: predict-and-reconcile where you can, show an honest pending state where you cannot, key your retries, and resolve a real conflict legibly — proving each behaviour with a reproducible test.

Project
0 of 6
Objective

Build a task/notes app over a deliberately asynchronous backend (POST returns 202, a consumer applies the write ~700ms later, with an injectable failure rate) so that every user action feels responsive and no failure mode — fake success, infinite spinner, double-submit, silent overwrite, read-your-writes violation — can occur.

Requirements
Acceptance criteria
  • A demo (or test) showing a failed optimistic write rolling back cleanly to the pre-mutation state, with no orphaned prediction left on screen.
  • A test proving the timeout path fires when the consumer never confirms — the pending state resolves to a 'still processing / retry' affordance rather than spinning forever.
  • A test proving two rapid submits with the same idempotency key produce exactly one server effect (one row, one charge), not two.
  • A demo showing read-your-own-writes holds: the user's change stays visible across the consistency window and the eventual refresh reconciles to it rather than erasing it.
  • A short write-up mapping each UI behaviour to the unit's rule (predict vs pending, when you keep the prediction vs the reconciled value, why the conflict was resolved the way it was).
Senior stretch
  • Replace polling reconciliation with a push channel (WebSocket/SSE) that signals when the consumer finished, and show the consistency window close on the event instead of on a timer.
  • Add a real CRDT (e.g. Yjs/Automerge) for a collaborative text field and demonstrate two clients converging to the same document with no lost edits, then compare against the LWW version.
  • Add an offline queue: writes made while offline are echoed locally, queued with their idempotency keys, and flushed in order on reconnect without duplicating effects.
  • Add observability: surface the live consistency-window latency (accepted → readable) as a metric and chart its p50/p99, so you can see the gap your UX is hiding.
Recap

This is the loop you will run on every async-backed feature: decide per action whether the result is predictable (optimistic UI, with the rollback the contract requires) or the server’s to decide (an honest pending state guarded by a timeout), key the retries so a double-click cannot double-charge, echo writes locally so read-your-own-writes survives the consistency window, and resolve conflicts legibly instead of silently overwriting. Building it once on a deliberately laggy backend makes the production version — where the lag is real and intermittent — muscle memory.

Continue the climb ↑End-to-end order pipeline: where each delivery guarantee lives
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.