Most teams working with AI coding agents are still optimizing the wrong thing. They focus on outputs. Better prompts. Better models. Better tools. And sometimes it works.
But that success is misleading.

The problem is this: you’re asking a probabilistic system to behave deterministically. No amount of prompt engineering can eliminate that.

I recently came across the idea of design-first collaboration — from Martin Fowler. At first glance it sounds obvious: don’t jump straight into code. Align on the problem. Explore the design. Agree on an approach. Like any experienced engineers would do. And it helps. A lot. Because it reduces ambiguity.

But even with perfect alignment, the model can still be wrong.
Not because it’s bad.
Because that’s what probabilistic systems do.

So you need a second layer. Not better prompts — better system design. This is where most teams stop too early. They improve how they talk to the model, but not how the system behaves when the model is wrong. Reliable AI systems don’t come from making the model smarter. They come from making the system tolerate mistakes: retries, validation, tests as a source of truth, deterministic workflows around probabilistic components.

Design-first reduces the space of possible errors. A harness contains the ones that remain. Without design-first, you’re running a harness against an undefined problem. Without a harness, design-first just gives you cleaner failures. Together, they start to look like engineering.

Design-first doesn’t make the model right. It makes the problem legible. The harness makes the system reliable. You need both.

The shift is subtle, but it matters: Stop asking “Did the model generate correct code?” Start asking “Did the system converge to a correct result?”

Implications

If you’re building with AI coding agents, this reframes what you optimize for.

Prompts are not the primary interface — they’re inputs into a system that must be validated. Correctness isn’t a property of a single response. It’s something the system either produces or doesn’t — across retries, checkpoints, and tests that function as contracts.

That means separating three concerns that often get collapsed: what should be built, what the model generates, and what gets accepted as correct. When those blur together, wrong outputs get blamed on the model. Usually it’s a wrong system. Assume failure as the default. Design so the system converges anyway.

You don’t need perfect outputs. You need a system that can absorb imperfect ones and still produce correct results.