English Is Not Specific Enough for Code
The Regression#
We spent fifty years building languages to escape the ambiguity of English, and then we volunteered to go back.
This is not a metaphor. Every syntax rule, every type system, every compiler error exists because English was never precise enough to instruct a logical system, and that imprecision is the entire reason programming languages were invented. Fortran did not emerge from an aesthetic preference for arcane notation. It emerged because the alternative, describing computational behavior in natural language, produced ambiguity that machines could not resolve and humans could not agree on. Every language since has been an elaboration of the same idea: force the programmer to be specific, and the machine will do what you mean.
The claim underneath AI-assisted development is that we no longer need that forcing function. That we can describe behavior in natural language, hand it to a model, and receive correct code in return. It is worth examining what that claim requires to be true.
Ambiguity Is a Feature, Until It Isn’t#
English is ambiguous by design. That ambiguity is not a flaw in the language. It is what makes the language efficient. When you tell a colleague to “handle the edge cases,” you are not providing a complete specification. You are trusting that shared context, professional experience, and a reasonable interpretation of your intent will fill the gaps. That trust is usually warranted because your colleague can ask a clarifying question, draw on domain knowledge, and apply judgment when the right answer is unclear. The ambiguity in the instruction gets resolved by a mind capable of resolving it.
A logical system has none of those resources. A compiler, a runtime, a machine of any kind cannot consult shared context. It cannot ask what you meant. It executes what it receives with no capacity to prefer the interpretation you intended over the interpretation you expressed, because those two things look identical to it. This is precisely what Dijkstra argued in his essay on natural-language programming. The formality that makes programming languages feel demanding to newcomers is not historical baggage waiting to be shed. It is the mechanism by which ambiguity is removed before it can cause harm. The precision is the value. Treating it as an obstacle misunderstands what a programming language does.
We live inside linguistic ambiguity comfortably because we are surrounded by minds that resolve it. We only notice the cost when we need a system, legal or logical, to be unambiguous and discover how hard that actually is.
The Ceiling of English Precision#
When society needs English to behave like a specification language, it produces more English. Contracts grow into hundreds of pages. Statutes accumulate amendments and definitions sections. Legal teams spend months negotiating individual words. And still it fails.
In 2017, a group of dairy truck drivers in Maine won a federal appeals court case largely on the basis of a missing Oxford comma. Maine’s overtime law exempted certain activities from overtime pay, and the final two items in the list were not separated by a comma in a way that made the scope of the exemption genuinely ambiguous. The court found that the ambiguity resolved in favor of the drivers. The resulting settlement cost the dairy approximately five million dollars. This was English operating at its highest register, drafted by professionals with legal training, the full weight of legislative convention behind it, and the specific intent to create a binding legal document. A missing comma cost five million dollars because the notation could not prevent it.
That is not a cautionary story about careless writing. It is a demonstration of the ceiling. English was not designed to be unambiguous, and no amount of professional care can fully overcome that design. The ceiling is structural, not a matter of effort.
A Translation Across a Precision Gap#
A large language model takes English as input and produces code as output. That translation moves across a precision gap, from a notation designed to tolerate ambiguity to one designed to eliminate it, and the source notation cannot close the gap from its side. This is not a limitation that scales away with the next model release. It is a property of the input.
The reason small, tightly scoped tasks work well with AI assistance is exactly this. I’ve written more about why smaller things get better results from AI, but the short version is that a narrow prompt reduces the space of reasonable interpretations, which reduces the surface area of the precision gap. When the task is small enough, many different possible outputs all represent a valid solution, and the model can find one. The ambiguity is present but inconsequential.
Vibe coding extends this observation in a direction that hides the problem rather than solving it. A vague prompt succeeds when the space of acceptable outputs is wide enough to contain whatever the model produces. The bar shifts from “did the model understand exactly what I meant” to “did the model produce something I can accept,” and those are not the same question. The ambiguity does not disappear. It gets deferred into acceptance criteria loose enough to absorb it, which means it shows up later, in production, in edge cases, in the places where the difference between what you said and what you meant turns out to matter.
Both Roads Lead to Programming#
There are two available responses to the ambiguity problem when you are specifying code behavior in natural language.
The first is to under-specify. Leave the model to resolve ambiguity on its own. When it resolves ambiguity incorrectly, the gap between what you said and what you meant becomes a bug. Not necessarily an obvious bug. Often the most consequential kind: subtle, context-dependent, surface-stable until a specific condition is met. This is the ordinary failure mode of relying on natural language for precise specification.
The second response is to over-specify. Add conditions. Clarify edge cases. Make explicit what you previously left implicit. Keep adding constraints until the prompt admits only one reasonable reading. Keep narrowing until the model cannot misunderstand you.
When you finish, you have written the program. Not in a language a compiler will check, not in a notation a type system can reason about, not in a form that can be diffed, tested, or statically analyzed. In English, which offers none of those properties. The specification precise enough to guarantee correctness is the program. That is the whole argument, and the only thing that changes as you over-specify is whether you acknowledge what you are doing.
Both roads arrive at the same destination. One road paves the remaining distance with bugs. The other paves it with prose.
The Hard Part Was Never the Syntax#
The hard part of software development was never remembering semicolons. Nobody’s career was limited by their ability to recall language syntax. The hard part was always the precise specification of behavior, the complete and unambiguous description of what a system should do under every condition it might encounter. That work is difficult not because of arbitrary notational complexity but because the problem space is genuinely complex, and no notation can make the complexity disappear.
What natural language prompting has done is substitute a notation designed to tolerate ambiguity for one designed to eliminate it. The constraint did not move. The work did not become easier. The notation just got worse at surfacing the places where the work still has to happen. As I described when looking at how principles become the constraint when the economics shift, removing one obstacle does not eliminate the constraint. It reveals the next one. The next one was always the specification, which is why this is not a new problem. It is the original problem, wearing a new interface.
We replaced a notation designed to eliminate ambiguity with one designed to tolerate it and called the swap progress.
The Moment You Will Recognize#
There is a moment you will encounter if you pay attention to it. You start with a natural language prompt. The model produces something almost right. You add a clarification. Another clause. An edge case. A constraint. At some point in that sequence, without a ceremony to mark it, you stopped describing a goal and started specifying behavior. You crossed the line from intent into implementation. You started programming.
When you notice that moment, the only useful question is whether English is still the right language to continue in. A programming language earns its keep not by being convenient to type but by refusing to let you express ambiguity. Every type error is an ambiguity the notation caught before it became a bug. Every constraint the compiler enforces is a piece of imprecision it would not let you ship. The value of a programming language is the ambiguity it refuses to let you express.
Judge your AI workflow by whether it preserves that refusal or launders it away into prose that feels precise until a production incident proves otherwise.