The Optimistic UI State
The interface answers in zero milliseconds. The network settles up in the background.
The incident
On office wifi the app felt fine. On a 2G connection in the field it felt broken: every tap waited three seconds for a server round-trip before showing anything. Users concluded taps weren't registering — so they tapped again, submitting everything twice.
Why the obvious approach fails
Waiting for 200 OK before updating the UI couples perceived performance to network latency — a variable you don't control and your worst-connected users experience at its worst. But naive optimism fails too: update the UI, fire-and-forget the request, and a failed write leaves the interface lying about state. Real optimistic UI is a discipline: immediate local mutation, a queued background request, and a rollback path that's honest when reality disagrees.
The standard
- User-initiated mutations update local state in 0ms; no interaction blocks on the network.
- The mutation is queued durably (surviving app restarts) with retry semantics from the Retry Helix.
- Every optimistic mutation has a defined rollback: on server rejection, the UI reverts and the user is notified with a retry path.
- Server truth wins on conflict — the optimistic layer never silently overrides an authoritative rejection.
- Pending-state is visible where it matters (a subtle "syncing" affordance), so users on bad networks aren't guessing.
How Cortex validates it
At plan time
Interaction-heavy blueprints declare which mutations are optimistic and define each rollback behavior.
At validation
assert: ui_feedback_latency(mutations) == 0msassert: server_reject → state_reverted && user_notifiedassert: mutation_queue.survives(restart)Assertion syntax is illustrative.
At review
The approver sees the throttled-network test run: 2G profile, instant UI, correct rollback on injected failures.