Let agents implement specifications rather than infer them

Aug, 2026

Since AI-assisted coding became mainstream, developers have debated how best to work with coding agents and how closely to review their output. I believe part of the disagreement comes from treating all delegation to an agent as equivalent.

These discussions often blur two different tasks: deciding what the software should do and implementing those decisions. I will call the first specification \(S\) and the second implementation \(C\).

In this post, by specification, I mean the collection of requirements, constraints, assumptions, and design decisions that determine what counts as an acceptable implementation. This may include, among other things:

The prompt \(P\) does not usually fully determine \(S\):

This distinction matters: in the first two cases, the agent is trying to recover the user’s intent. In the last, it is doing design work on the user’s behalf.

We can therefore treat the specification \(S\) as a latent (unobserved) variable. Given \(P\), many specifications may remain possible; we can represent this uncertainty as \(p(S\mid P)\). As a conceptual decomposition, the process can be seen as:

\[ P \rightarrow p(S\mid P) \rightarrow C \]

With a vague prompt such as Add caching to the user lookup endpoint \(p(S\mid P)\) is broad since the prompt is compatible with many specifications (local or shared cache, different TTLs, different invalidation policies). Although the model does not explicitly select a coherent specification, generation still requires it to make concrete choices in the code (it is worth noting that reasoning models may explicitly consider some of those choices before implementation).

Generating an implementation forces the model to commit to particular choices. Those choices will often reflect common patterns from training or conventions in the surrounding code, whether or not they match what the user would have chosen. The result is a plausible interpretation of \(P\) that encodes design decisions the user never explicitly made. Those decisions may be suboptimal because, where task-specific knowledge is unavailable, the model must rely on learned defaults and on the surrounding code.

The situation changes when the user has a clear model of the system they want to build. The prompt can then encode architectural choices, constraints, invariants, ideas, and other important design decisions. As a result, \(p(S\mid P)\) becomes more concentrated, and the agent has less freedom to decide what the software should be. Ideally, whatever remains underspecified is also less important.

This brings us to the infamous question: should we read AI-generated code? I believe it depends:

This reduces the need to read code in order to discover design decisions. It does not eliminate the need for review since security, maintainability, integration, observability, and performance may still require direct inspection.

However, this line of thinking helps us distinguish two distinct failure modes:

Controlling the specification reduces the first kind of failure but does not eliminate the second. Tests, invariants, type systems, static analysis, and other established software engineering practices help address implementation failures.

From this perspective, “control the ideas, not the code” is not as radical as it sounds. Given a limited attention budget, human effort often has greater leverage when spent making important decisions explicit and their consequences verifiable, controlling \(S\) rather than writing \(C\). The less confidently those decisions can be specified and checked, the more important direct inspection of the generated code remains.