🚧 Designers never finish their own portfolio. This one ships rough on purpose and gets better in public. If something looks half-done, it probably is, and I'm on it.🚧 Designers never finish their own portfolio. This one ships rough on purpose and gets better in public. If something looks half-done, it probably is, and I'm on it.🚧 Designers never finish their own portfolio. This one ships rough on purpose and gets better in public. If something looks half-done, it probably is, and I'm on it.🚧 Designers never finish their own portfolio. This one ships rough on purpose and gets better in public. If something looks half-done, it probably is, and I'm on it.🚧 Designers never finish their own portfolio. This one ships rough on purpose and gets better in public. If something looks half-done, it probably is, and I'm on it.🚧 Designers never finish their own portfolio. This one ships rough on purpose and gets better in public. If something looks half-done, it probably is, and I'm on it.
Open menu
Switch to Darkhello@product.inc

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

Rendering diagram…

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

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.