
In the shadow of the Claude Sonnet 5 release (some might say this is equivalent to Opus 4.8 in some areas but cheaper) and Fable 5 around the corner, the question is which tool/model cuts better and which requires more human effort.
Also, there’s a comfortable assumption doing the rounds:
that as the models get better, we can afford to be vaguer. Point a capable enough system at a rough idea, the thinking goes, and it will figure out the rest. Prompting becomes the skill; understanding becomes optional.
I’ve come to believe quite the opposite. A more capable model doesn’t rescue a poorly framed problem — it amplifies whatever you hand it, ambiguity included.
Literally: mess in, mess out — or, in harsher terms, garbage in, garbage out.
Give a powerful system a fuzzy problem and it won’t fail loudly. It will succeed, confidently, at the wrong thing. The output will be fluent, well-structured, and plausible enough to survive a casual read. That’s precisely what makes it dangerous.
So if the model isn’t the variable that decides quality, then what is? In my experience, two things. How well you bound the problem, and how much foundational knowledge you bring to bounding and checking it. Spec-Driven Development is really just the discipline that forces both to happen up front, on purpose, instead of leaking out later as rework.
The spec is a boundary, not a document
It’s tempting to treat a spec as documentation — a description of what you’re about to build. A good spec is a boundary. It draws the edges of the problem:
What’s in scope, what’s explicitly out, what the interfaces are, which constraints are load-bearing, what “correct” looks like when you’re done.
The reason this matters mechanically comes down to how these systems work. A model generates by pattern-matching against everything it has seen, and where your problem is under-specified, it fills the gap with its priors — the most statistically reasonable completion.
For generic problems, those priors are often fine. For unique systems — legacy platforms, unusual domain rules, anything the model wasn’t trained on — the plausible answer and the correct answer diverge, and nothing in the output tells you which one you got.
A boundary closes those gaps before they’re filled with guesses. Think of it as a fence rather than a blueprint. Inside a well-placed fence, the model can run at full speed and you’re happy to let it — the whole point of the fence is that you know exactly where the edges are. Take the fence away and the speed stops being an asset. It just means the model reaches the wrong place faster.
The boundary does several jobs at once. It collapses the solution space so generation actually converges instead of wandering. It creates something you can validate against — you can’t verify output against a problem whose edges were never drawn, which is the quiet prerequisite the generate-then-validate loop depends on. It makes the work decomposable. And it moves disagreement upstream, where it’s cheap: people argue about the spec in an afternoon rather than about merged code three weeks later.
The hardest part of drawing a boundary is almost always the negative space — stating what the system should not do, what’s deliberately excluded, which tempting complementary feature is out of scope.
In a nutshell, draw the fence first. Then you can trust the speed.

Leave a Reply