Everyone with an AI subscription can start a build. A model, a prompt, a webhook, and an afternoon will get you a demo that looks finished. Almost nobody finishes one. The distance between those two states is not more prompt engineering — it is everything a demo is allowed to skip.
A demo has no memory of failure
In a demo, the happy path is the only path anyone exercises. The WhatsApp message arrives, the model replies, everyone nods. Nobody sends a message while the process is mid-restart. Nobody hits the provider on the one day its API returns a 500 for ninety seconds. Nobody double-taps send because the first attempt looked like it hung.
In production, all three of those happen in the first week, and the system has to have an opinion about each one:
- Retries have to be bounded and have to back off, or a flaky downstream call turns one failure into a retry storm that looks, from the outside, like an outage you caused.
- Idempotency has to be designed in, not bolted on. If a webhook can fire twice — and every webhook can fire twice — then "send the confirmation email" has to be safe to run twice, which usually means keying the side effect to something stable (a message id, a booking id) and checking before you act, not after.
- Timeouts need a default that isn't "wait forever." A model call that's still running at the ninety-second mark is not a slow success, it's a failure the user is currently experiencing as silence.
None of this shows up in a five-minute walkthrough. All of it shows up in month two.
Multi-tenancy turns bugs into incidents
A single-customer prototype can get away with a shared config, a shared rate limit, a shared log stream. The moment there is a second tenant, every one of those becomes a boundary that has to hold: one tenant's traffic spike cannot degrade another's, one tenant's bad data cannot leak into another's context window, one tenant's support ticket needs to be answerable from logs without reading someone else's conversation to find it.
This is where a lot of "it worked in the pilot" builds actually break — not because the model got worse, but because the pilot was implicitly single-tenant and the real system isn't.
Observability is the difference between a bug and a mystery
When a demo goes wrong, the person running it is watching the screen and can just retry. When a production workflow goes wrong at 2am for a customer nobody is watching, the only way to find out is if the system said something on its way down. That means:
- structured logs that tie a failure back to a specific tenant, conversation, and step — not a stack trace with no context,
- an alert that fires on "this queue is backing up" before a human notices "customers are complaining,"
- a way to answer "what did the system tell this customer, and why" without re-running the whole pipeline by hand.
Skipping this is the single most common reason a working build quietly stops working and nobody notices until a customer says so.
Handover is a feature, not a fallback
The best production AI workflows are not the ones that never need a human — they're the ones that know precisely when they don't know, and hand off cleanly with full context instead of either guessing or going silent. Building that boundary well — deciding what "confident enough" means for a given task, and making the handoff carry everything a human needs to pick it up cold — is harder than building the happy path, and it's usually the part that gets cut when a project is scoped like a demo instead of like a system.
None of this is a reason not to build. It's the reason the diagnostic comes before the build: you find out, in the client's own numbers, whether the leak is worth fixing before anyone commits to fixing it properly — and "properly" always means the list above, not just the model call in the middle of it.
