SayCraft

← Blog

Making an AI Coding Agent Reliable in Production

By SayCraft Team · 2026-06-25 · 6 min read

I build SayCraft, an AI app builder you drive by talking — you open a meeting, talk through what you want, and it builds a working web app live. The part nobody asks about, but that ate most of my engineering time, is the layer underneath: SayCraft is built on an LLM coding agent, and getting a coding agent to behave reliably in production is a very different problem from getting it to work in a demo.

Here are the things I got wrong first, and what actually fixed them.

1. The model is not your reliability layer. Your code is.

My first instinct was to write a great system prompt and trust the agent to follow it. “Always do X. Never do Y. Build the UI before the logic.” It works in a demo and silently drifts in production. A small model under load will, three turns later, quietly stop doing X.

The fix was boring and correct: encode invariants in code, not prose. If something must happen, it becomes a deterministic trigger, a schema constraint, or a guard — not a sentence in a prompt. The prompt explains intent; the code enforces it. Once I moved the load-bearing rules out of the prompt and into validators and state machines, the flakiness mostly went away.

2. Strict schemas + self-repair beats loose schemas.

When you ask a model for structured output, small models drift on field names — you ask for category and get classification, you ask for rationale and get confidence. The tempting fix is to relax your schema to accept all the variants. Don't. Six months later nobody can tell which field is canonical, and your traces look healthy while every call is silently burning retries.

Keep the schema strict. When validation fails, feed the validation error back to the model and ask it to correct its own output: “your previous output failed because X, here is the exact required shape, output corrected JSON only.” Retry a few times, then fall back to a deterministic path and log loudly. You get strictness and resilience, and your failures stay observable.

3. Render is king — don't block on pedantry.

For a tool where users watch the app appear live, latency to something visible matters more than correctness of every type annotation. I used to run a full build and block on any error. Now the agent type-checks only, the build config is lenient where it can be, and if the UI renders, I don't stop the turn over a pedantic warning. UI first, with placeholder images, then real assets, then the invisible logic. People forgive an imperfect type; they don't forgive a blank screen for 90 seconds.

4. Long-lived sessions beat re-spawning.

Early on I spawned a fresh agent per request. It was simpler to reason about and much slower — cold-start cost on every interaction, and lost context between turns. Moving to one long-lived agent process per meeting, with --resume to batch up new requirements that arrive mid-build, cut latency hard. The subtle bug: a new requirement pushed into an in-flight turn gets merged and dropped. So new requirements stay pending and get picked up as one batch when the current turn ends. That's the kind of concurrency rule you can only learn by watching it break in production.

5. Measure before you “improve” the context.

I was sure that injecting a richer context snapshot into each agent call would make outputs better. I measured it instead of assuming — and a 2–8KB snapshot tripled time-to-first-token on the model I was using. The “obvious improvement” made the product slower in a way users would feel. Now I measure end-to-end latency against the exact production task before shipping any change that puts more into the agent's input.

The through-line

A coding agent is a probabilistic component in a system that has to be deterministic at the seams. The coding agent does the hard part — writing the code — genuinely well. My job turned out to be building the boring scaffolding around it: strict contracts, code-level invariants, fast-feedback loops, and honest measurement. If you're shipping anything agent-powered, budget most of your time for that scaffolding, not the prompt.

That scaffolding is exactly what lets SayCraft turn a messy live conversation into a working web app you can see and share in seconds. If you're curious where it fits among the best vibe coding tools, the fastest way to understand it is to watch it happen.

Build by talking

Frequently asked questions

What does it mean to build a product on an AI coding agent?

It means the agent — an LLM that can write and edit code — is a core runtime component of your product, not just a tool you use at your desk. Users' requests flow into it and its output ships to them, so its reliability becomes your product's reliability. That's a very different bar from using a coding agent to help you write code in a demo.

Why isn't a great system prompt enough to make it reliable?

A prompt explains intent, but a small model under load will quietly drift from it a few turns later. Load-bearing rules belong in code — deterministic triggers, schema constraints, and guards the model can't silently ignore. The prompt should describe what to do; your code should enforce it.

How do you keep an LLM's structured output reliable?

Keep the schema strict instead of loosening it to accept the model's drift. When validation fails, feed the error back to the model and ask it to correct its own output, retry a few times, then fall back to a deterministic path and log loudly. You get strictness and resilience, and failures stay observable.

Is an AI coding agent reliable enough for production?

Yes — if you treat it as a probabilistic component inside a deterministic system. The agent writes the code well; the reliability comes from the boring scaffolding around it: strict contracts, code-level invariants, fast-feedback loops, and honest measurement. That scaffolding, not the model, is where most of the engineering work lives.