🚧 Under construction β€” I'm migrating this site from Framer to Next.js and publishing it early for testing, so a lot of the content is still in flux.🚧 Under construction β€” I'm migrating this site from Framer to Next.js and publishing it early for testing, so a lot of the content is still in flux.🚧 Under construction β€” I'm migrating this site from Framer to Next.js and publishing it early for testing, so a lot of the content is still in flux.🚧 Under construction β€” I'm migrating this site from Framer to Next.js and publishing it early for testing, so a lot of the content is still in flux.🚧 Under construction β€” I'm migrating this site from Framer to Next.js and publishing it early for testing, so a lot of the content is still in flux.🚧 Under construction β€” I'm migrating this site from Framer to Next.js and publishing it early for testing, so a lot of the content is still in flux.
Open menu
Switch to Darkhello@product.inc
AIUX patterns

Streaming is a commitment

Tags: streaming, latency, chat

Streaming an answer buys perceived speed and spends the ability to revise it. Text that has appeared has been read, and it cannot be taken back without the user watching it be taken back. Every check you would have run on the finished answer now runs before the first token, or it does not run at all.

The affordance is not the decision

Showing tokens as they arrive is a component. It is documented everywhere and it takes an afternoon.

The decision underneath it is what nobody writes down: streaming converts an atomic answer into a public draft. A buffered call produces one event β€” here is the answer, or here is an error. A streamed call produces a stream of small irreversible publications, each of which the user has already acted on by the time the next arrives.

Four things follow, and none of them are visible while you are building the happy path.

You cannot validate after the fact. A schema check, a late-firing guardrail, a citation that turns out not to resolve, a total that does not reconcile β€” all of it has to pass before the first token or be abandoned. A product that streams and then corrects reads as unreliable even when the corrected answer is right, because the correction is the more memorable event.

You cannot reorder. Streamed output commits to its own structure as it goes. If the best version of the answer needs its conclusion at the top, you either buffer until you know what the conclusion is, or you accept a shape the model can commit to from the first token.

The mid-stream failure is worse than the buffered one. A buffered call that fails leaves an error, which is legible. A stream that dies at sixty percent leaves something that looks like an answer and reads like one. It is the more dangerous failure and it is the one that gets designed last, if at all.

Length becomes a thing the user watches. Under a buffered call, a long answer and a short one cost the same attention. Under a stream, the user sits through the difference.

The fork

Rendering diagram…

The middle option is the one worth reaching for first: stream the work and buffer the artifact. Show progress as it happens β€” steps taken, sources opened, what the system is doing now β€” and deliver the result in one piece when it is whole. That keeps the responsiveness, which is the thing streaming was actually bought for, without making a draft public.

What streaming needs that buffering does not

Prior art, and where this differs

There is nothing wrong with the entry as documentation β€” token-by-token delivery is a real component and it is worth having a name for. But a catalog of things you can add cannot tell you when adding one is the wrong call, and streaming is the clearest case in the category: it is the default nobody argues with, and it is wrong for a meaningful share of the outputs it gets applied to.

Grounded in

Poppy streams model responses into a two-panel thread. The streaming was the cheap part. What made it usable was what happens when the stream stops: the thread persists and titles itself from the first message, so a teacher who dictated a parent email between classes can find it again the following week. Building the stream took an afternoon; deciding that a streamed answer had to become a durable, findable artifact is the part that was actually design.

The same product is the reason the fork above is not theoretical. Poppy's four one-tap actions produce short, structured outputs β€” a quiz, a reading-level adaptation β€” and streaming those is worse than not streaming them.

Anti-patterns

The smallest version worth building

A two-column list of every output type in the product, marked streams or buffers, with one line of reasoning each. Run the fork above down it once.

Then, for everything in the streams column, two things that do not exist yet: a visible terminal state, and a screen for the stream that died at sixty percent. That second one is an afternoon, and it is the only part of this anyone will thank you for.