Don't animate a guess
Tags: realtime, progress, honesty
Motion reads as evidence. A bar filling, a vehicle gliding along a route, a counter ticking up β people take each of those as a report of real progress. When the motion is invented on the client, it keeps reporting progress through a stall, a failure or a dropped connection, and the user finds out otherwise only when the result does not arrive.
Why fake motion is worse than no motion
A static screen during a wait is honest about one thing: nothing visible is happening. It is uncomfortable, but it does not lie.
Simulated motion is more comfortable and less honest. It tells the user the system is working, that the work is advancing at a particular rate, and β by the shape of a bar β roughly how much remains. If none of that is backed by real state, every one of those claims is fabricated, and they are fabricated in the most persuasive medium an interface has.
It also fails in the worst direction. The smoother the fake, the more convincing it is, and the worse the moment it is discovered. A progress bar that fills to 90% and sits there teaches the user that every bar in the product is decoration. A map of vehicles that keeps moving through an outage teaches them the map cannot be relied on in exactly the situation they would need it for.
Generated output makes this tempting
Model calls are slow and their duration is unpredictable, so there is real pressure to fill the wait with something. The easy fill is a determinate-looking animation on a timer: a bar that reaches most of the way in roughly the average response time.
That is a guess about duration, animated. When the call runs long, the bar lies. When the call fails, the bar keeps lying until something times out.
Drive motion from state
Where the system has a real signal β bytes received, steps completed, records written β motion should be driven by it and should stop when it stops. A stalled signal should look stalled.
Where there is no signal, the honest representation is indeterminate: a spinner, a pulse, a sentence saying what is happening. Indeterminate is not a failure of design. It is the correct answer to how far along is this when the true answer is unknown.
Optimistic updates are the legitimate exception, and worth separating out: showing a change immediately and reconciling it a moment later is fine when the guess is almost always right and a wrong one is corrected quickly. It stops being fine when the correction arrives long after the user has acted on what they saw.
Grounded in
Motive is a live fleet-tracking map, and the obvious way to build it is to animate trucks along their routes on the client, at plausible speeds, forever.
It doesn't. Vehicle movement is streamed from the database β Postgres LISTEN/NOTIFY piped to Server-Sent Events β so the map reflects state a backend could just as easily be writing. The consequence is the whole point: when nothing is arriving, nothing moves. A stalled feed looks stalled, rather than like a fleet that is still driving, which is precisely the difference an operator watching that map would need to see.
Anti-patterns
- A determinate progress bar on a timer. A duration guess, drawn as a measurement.
- Motion that continues through a disconnect. The interface keeps reporting activity the system has no evidence of.
- The bar that stalls at 90%. Teaches the user that every bar in the product is decoration.
- Typewriter effects over text already received. Animating output the system has finished is performing work that is not happening β see streaming is a commitment.
- Optimism with slow reconciliation. Showing a change as done, then quietly reverting it minutes later.
The smallest version worth building
Find every animation in the product that implies progress. For each, name the real signal driving it. Where there isn't one, replace the determinate animation with an indeterminate one.
Then add the state most products skip: when the signal stops for longer than usual, say so. "Still waiting β this is taking longer than normal" is more reassuring than a bar that has quietly stopped moving.
Related patterns
- Show the machine listening β the same rule for input: derive the signal from what is actually arriving, not from a timer.
- Streaming is a commitment β where real incremental output exists, stream it; where it doesn't, don't fake it.
- Design the failure state first β the stalled feed is a failure state, and it needs to look like one.