Back to blog

Why I Want My Coding Agent to Fail Loudly — On Purpose

Sep 4, 2026
Why I Want My Coding Agent to Fail Loudly — On Purpose

Same task. Same agent. Same repo. Two prompts, one word apart in spirit but not in outcome. The first produced code that looked finished, sailed through review, and two days later let a timed-out worker retry forever in production. The second made the agent stop mid-task and tell me, in plain language, exactly what it couldn't get right. The second one is the win — not because the code was better, but because I found out immediately instead of from an incident channel two days later.

You've heard the standard advice: be more specific, add more steps, describe the process in more detail. I did all of that. It didn't fix anything, because the problem was never resolution — it was that I was describing a route, and the agent doesn't need a route. It needs something to check its own work against.

The optimizer has no target

An agent is an optimizer. Every step is a bet on "does this move me toward satisfying the prompt." When the prompt describes a process — do X, then Y, then Z — there's no independent signal for "satisfying the prompt" other than the text of the process itself. So the agent optimizes against the prose. It produces a trajectory that reads as if it followed your steps: right function names, right control-flow shape, comments that echo your own language back at you. That trajectory can be completely wrong on the one dimension you actually cared about, and the prompt gives it nothing to catch that with — because the prompt never said what "wrong" would even look like.

That's why "write clearer steps" plateaus fast. More detail in a process description just hands the optimizer more prose to pattern-match against — more surface area for a plausible-looking wrong answer, not less. You're tightening the wrong constraint.

Stop writing directions. Write a finish line.

Here's the same task — add rate limiting to a login endpoint — written the two ways that produced my two outcomes.

text
PROCESS PROMPT (what most people write):
"Add rate limiting to POST /login. Use a token bucket algorithm,
store the bucket state in Redis, check it before the handler runs,
and return an error if the limit is exceeded."

END-STATE PROMPT (what actually works):
"POST /login must return 429 on the 11th request from the same
IP within 60 seconds. Write a test that fires 15 requests in a
loop and asserts requests 1-10 return normally and 11-15 return
429. All existing auth tests must still pass unmodified. Do not
add rate limiting to any other route. Do not change the response
shape of a successful login."

The process prompt got me a token bucket that reset on every single request — the "store in Redis" step got implemented with a fresh cache key generated per request instead of a stable key per IP. Structurally, it followed every instruction I gave. Nothing in the prompt could have caught that, because nothing in the prompt was checkable. The end-state prompt got the agent to write the 15-request test first, run it, watch it fail at request 11 with a 200 it shouldn't have gotten, and fix the Redis key bug before it ever showed me anything.

What a checkable end-state is actually made of

  • A concrete test or assertion the agent can run against itself — not "it should rate limit" but "the 11th request in 60 seconds returns 429, verified by a test the agent writes and runs before declaring done."
  • Explicit invariants — what must still be true afterward. "All existing auth tests pass." "P50 latency on /login doesn't regress." These are the guardrails stopping a technically-passing solution from quietly breaking something you didn't think to test.
  • A negative space — what the agent must not do to get there. "Do not rate-limit other routes." "Do not introduce a new dependency." This is the one people skip, and it's the one that rules out the plausible-but-wrong path — because the shortest route to satisfying a vague positive spec very often runs straight through the thing you'd have vetoed if anyone had asked you.

When you genuinely can't make it checkable yet

Exploratory and design work doesn't have a test sitting there waiting to be written — you don't know the shape of the answer yet, so you can't specify its finish line. Don't fake a checkable spec here; you'll anchor the agent on a criterion you invented under pressure, and it'll optimize for that instead of the real thing. Flip the order instead: ask the agent to propose the acceptance criteria first — "before writing any code, tell me how you'd verify this is done, and what you'd measure" — and review those criteria yourself before it touches a single file. You're still writing a finish line. You're just letting the first draft come from the agent, because at that point it's read more of the relevant code than you have. The checkable spec still exists before the coding starts — you've just moved who drafts it.

A worked example from my own multi-agent work

This one's straight out of my orchestrator work: when a worker agent in a multi-agent pipeline times out on a subtask, the orchestrator needs to retry before giving up. I ran the same task three ways to see where each one broke.

text
1) VAGUE PROCESS:
"Add retry logic for when a worker agent times out."

2) DETAILED PROCESS:
"When a worker times out, catch the timeout, log it, retry up to
3 times with exponential backoff, then mark the subtask failed."

3) CHECKABLE END-STATE:
"Write these two tests and make them pass:
 (a) a worker that times out twice then succeeds on the 3rd call
     → orchestrator returns a success result, total attempts == 3
 (b) a worker that always times out
     → orchestrator marks the subtask failed within 3 attempts,
       does not hang, and total wall-clock time is bounded by the
       backoff schedule (assert it, don't eyeball it)
Invariant: the existing single-worker happy-path test still passes
unmodified. Do not change the message schema between orchestrator
and workers."

Version 1 produced a retry loop with no cap — it read "retry," grabbed the nearest while-True pattern from context, and called it done. Version 2, numbers spelled out, got the count right but reset the attempt counter per orchestrator cycle instead of per subtask, so concurrent subtasks silently shared a budget and one slow worker could starve another's retries. Invisible unless you went looking, and I hadn't specified anything that would surface it. Version 3 is the only one where the failure was mine to see, on my own screen, before it shipped: the agent's own test (b) failed on the first pass, because the shared counter caused test (a)'s worker to run out of attempts when the two ran in the same suite. It reported the failing assertion and why, I saw the shared-state bug sitting right there in the diff before it ever reached a PR, and the fix was one line — pass the counter as a parameter instead of holding it on the orchestrator instance.

Before you send your next prompt, check

  • Did I describe a test or assertion the agent can run against its own output — not just a description of correct behavior?
  • Did I state the invariants: what must still work, still pass, still be true after this change?
  • Did I name the negative space: what the agent must not touch, add, or change to get there?
  • If I can't write a checkable spec yet, did I ask the agent to propose acceptance criteria first, and approve them before any code got written?
  • Am I about to accept "looks done" as evidence, or did I actually watch it fail or pass a concrete check?

The tell that you specified the finish line right isn't that the output looks good — a plausible-looking wrong answer looks good too, that's the entire problem. The tell is that when the agent is wrong, you know it immediately, from a failing assertion or an honest "I couldn't satisfy the invariant," instead of finding out from a user report a week later. Loud failure is the feature. Design for it.

Discussion

Chat with Chi Cong (AI) about this article. Your conversation is private to you — you can publish a summary for others when you're done.

Ask me anything about “Why I Want My Coding Agent to Fail Loudly — On Purpose” — trade-offs, decisions, or the story behind it.