ARTICLE
Vibe Coding Is Not The Best We Can Do
Explore why vibe coding isn't the pinnacle of AI programming. Discover the importance of specification, verification, and tests in software engineering.

Dave Farley

When I talked about vibe coding at AI Native DevCon London, I was not trying to dismiss AI coding assistants. I use them. I think they are a major change in the way software will be produced. But I wanted to ask a harder question: if natural language and agents are becoming part of programming, what still has to remain durable?
My talk was called "Vibe Coding: Is this really the best we can do?" The point was not nostalgia for hand-written code. The point was that software engineering has always depended on specification, verification, feedback, and incremental learning. If AI speeds up code generation but weakens those practices, we have not made engineering better. We have moved the bottleneck somewhere more dangerous.
There are claims around AI programming that are partly true and still risky. Natural language programming has a place. AI agents can write a lot of code. AI-generated tests can sometimes help with characterization or refactoring. But if those ideas are treated as replacements for precise intent and independent verification, they become a bad trade.
Use This Talk As Agent Context
Tessl has turned my AI Native DevCon talk into a skill your agent can use as context. You can also watch the full recording.

What Are Tests For?
I started with tests because that is where I usually start. I am an old-school, card-carrying test-driven development developer, and I think tests are often misunderstood in AI coding conversations.
Tests are not mainly there to prove success. They are not there to challenge my genius as a programmer. They are a form of measurement. They are like the carpenter's tape measure: the thing that lets us check whether we are achieving the goal we intended to achieve.
That distinction matters because we cannot infer the goal from the solution. The solution can always be wrong. If I write a function called calculateTax and it returns 50 times the amount I put in, an AI system that only looks at the existing code can generate a test that preserves that behavior. That may be useful if I want to refactor without changing behavior. It is not useful if I want to know whether the tax calculation is correct.
That is the problem with treating AI-generated tests as a substitute for specification. If the code is the only input, the test can only verify that the code continues to do what the code already does. It may reinforce the wrongness. It may give us confidence in precisely the thing we should be questioning.
So yes, AI-generated tests have a place. But they are not the same as tests that express what we wanted before the solution existed. Specifying the goal is part of the job. If we skip that work, the tool cannot recover it for us.
What Is A Program For?
The next question I asked was what a program is for. It is tempting to say that a program exists to define a sequence of instructions, encode algorithms, or implement a design. Those are things a program does, but they are not the whole value.
A programming language has three important goals. First, it helps us organize our thinking about a problem. It gives us a way to explore the surface area of a problem with more precision than informal conversation usually allows.
Second, it communicates our understanding to other humans. A program is not only for the machine. It is also a technical explanation of what we understood, which tradeoffs we chose, and how the solution is supposed to behave.
Third, it tells the computer what to do. That is important, of course, but it is not the only purpose. Assembly language can tell the computer what to do. Most of us do not want to work there because higher-level languages help us think and communicate more effectively.
Programming languages also give us three techniques that matter deeply: a relatively simple and consistent grammar, a precise expression of intent, and repeatable deterministic execution. If we run the same program twice with the same inputs, we expect the same result. That is what makes reasoning, testing, and change possible.
Where Does Natural Language Fall Short?
Natural language is powerful, but it is not a drop-in replacement for programming languages. It is too vague, too open to interpretation, and too hard to execute deterministically.
We have all seen this in ordinary software work. Someone gives a vague instruction from above, a team builds what they understood, and everyone is disappointed because the goal was never precise enough. Natural language can communicate intent, but it often leaves too much room for interpretation.
That is why I do not like the simple claim that English is the programming language of the future. If we only chat with a computer about what we want, we should expect ambiguity. We may get something plausible, but it may not be repeatable. If we version-control the prompt, that does not necessarily mean we can reproduce the same result.
This does not mean we have to inspect every line of generated code forever. I am not arguing that. I have reached the point where AI writes a lot of code for me. But I am more precise than ordinary conversation when I specify what I want. I use a more prescriptive form of natural language, closer to a problem-specific specification than a casual prompt.
Vibe coding alone is not enough for serious systems. We need a way to specify intent more precisely, communicate that intent to humans, and verify that the machine produced the behavior we asked for.
What Problems Does AI Programming Create?
I framed AI programming around three problems.
The first problem is specification: how do we say what we want with enough precision? The second is verification: how do we confirm that we got what we wanted? The third is incrementalism: how do we keep learning and changing in small, controlled steps when agents can produce large changes very quickly?
That third problem matters more than it first appears. Human engineering is constrained by what fits in our heads. We learn by making small changes, evaluating those changes, and adjusting our understanding. Good software development is an incremental process of learning and discovery. I think that is true of engineering more broadly.
AI agents work differently. They can build a large context and then make a large change. If we have not specified the goal and cannot verify the result, the agent may produce a lot of code without helping us learn. Worse, it may regenerate or reshape the system in ways that make incremental understanding harder.
I mentioned someone using multiple agents who estimated that they were producing 12,000 lines of code per day. No human can review 12,000 lines of code per day in a meaningful way. No human can manually test that volume of behavior and reliably decide whether it is correct.
So the bottleneck is not code generation. We have sped up the coding bit, which was often not the hardest part. The bottleneck is specification, verification, and release. If we do not speed up assurance to match generation, we have only moved the constraint.
Executable Specifications Are The Program
The future I described is one where the program moves closer to a precise description of what we want, encoded as executable specifications, with AI translating those specifications into implementation.
In the past, a program was usually a precise solution encoded as algorithms. The problem we were trying to solve was often implicit in the solution and in the heads of the people who built it. In an AI-assisted workflow, that is not enough. The desired behavior needs to become explicit.
That is why behavior-driven development and acceptance testing are so relevant here. A BDD-style specification can describe the behavior we want in a form that is readable by humans and executable by machines. It can act as both the specification and the verification mechanism.
The programmer's job shifts. We design a problem-specific language for the behavior we want. We define the outcomes, edge cases, and non-functional requirements: performance, security, reliability, and any other architecture or design property that matters for this system.
Those details cannot be left for the AI to infer. The right level of security for a single-player game is not the same as the right level of security for a bank. The desired behavior depends on context, and context has to be specified.
In this model, AI generates solutions that satisfy the executable specifications. We can then run those specifications against the result and use test values the model has not seen, so it cannot simply game the visible examples. The point is not that AI disappears from the process. The point is that AI works inside a stronger specification and verification loop.
How Do We Keep Incrementalism?
The answer to the third problem is not new. It is continuous delivery, applied to an AI-native workflow.
In the past, good teams worked iteratively in small steps. They added tests and code, gathered feedback from deployment pipelines, treated each change as an experiment, and valued empirical learning.
In the future, that does not go away. We still work iteratively in small steps. The AI may generate the code, but we still gather feedback, validate every change, build systems incrementally, and treat changes as experiments.
That means version-controlling the specifications, because the specifications are now effectively part of the program. It means running the tests ourselves in a deployment pipeline rather than trusting the AI to tell us it passed. It may also mean keeping some tests hidden from the AI so it cannot shape the implementation only around the examples it has seen.
This is not a rejection of AI. It is the condition that makes AI useful for complex systems. If we can specify what we want precisely, verify that we got it, and preserve incremental learning, then the generated code becomes less important than the behavior we can prove.
That is why I compared this shift to the move from assembly language to higher-level languages. At first, programmers inspected the assembly generated by compilers. Most people do not do that anymore. They trust the compiler because they have other ways to reason about the result.
AI assistants may become similar. We may care less about the code they generate line by line, but only if we have a reliable way to verify the behavior we wanted. Without that, vibe coding is not the best we can do. It is just a faster way to create uncertainty.
I gave the full version of this argument at AI Native DevCon London. To go deeper, watch the full recording.
COPY & SHARE

Dave Farley
Dave Farley is a pioneer of Continuous Delivery, thought-leader and expert practitioner in CD, DevOps, TDD and software design. Dave has a long track record in creating high-performance teams, shaping organisations for success, and creating outstanding software.
READING
·
0%
IN THIS POST
COPY & SHARE

Dave Farley
Dave Farley is a pioneer of Continuous Delivery, thought-leader and expert practitioner in CD, DevOps, TDD and software design. Dave has a long track record in creating high-performance teams, shaping organisations for success, and creating outstanding software.