AI Is Not a Magic Wand Because Verification Has Always Been the Bottleneck
The prevailing belief that AI merely moves the bottleneck from writing code to checking it misses the point. Checking has always been the real constraint; we only disguised it with elaborate coding ceremonies for years.
A typical day for a software engineer now revolves around the rites of TDD or BDD. Much of that time is spent mechanically cycling through red‑green‑refactor steps instead of wrestling with the problem’s core logic. We drown in boilerplate and routine processes that rarely touch the true difficulty.
The rise of LLM agents such as Claude Code has spawned what I call “vibe coding.” A developer feeds a vague prompt, watches code appear, and hopes the result “feels” right. This shortcut sidesteps the essential complexity and is dangerous.
When the rituals — typing, scaffolding, common patterns — are handed to the model, the work does not disappear. The familiar distractions vanish, forcing a direct confrontation with the raw logic of the system. Hiding behind a flaky test suite or a tangled directory tree is no longer possible. The only remaining question is whether the logic holds and whether a change will ripple into distant modules.
In machine learning a harness guarantees reliable operation under defined conditions. In the AI‑driven workflow the programmer becomes that harness. The model supplies raw compute, but output quality is bounded by the rules, edge cases, and architectural principles you impose.
A fuzzy, “vibe” prompt produces fragile, “vibe” implementations. A near‑formal specification yields solid code.
Escaping the vibe trap means moving beyond TDD toward stronger verification. Instead of writing unit tests only for happy paths, I rely more on fuzzing and property‑based testing.
A practical verification approach
Generating a complex function with an LLM should not stop at “write tests.” That is another ritual. Adopt a more aggressive technique. In Python, go beyond the simplicity of unittest and use a tool that explores the state space more thoroughly:
import hypothesis
from hypothesis import given, strategies as st
# Rather than crafting manual unit tests, opt for property-based testing
# to compel the AI-generated code to expose its fragility in edge cases.
@given(st.lists(st.integers()))
def test_complex_logic_property(data):
result = my_ai_generated_function(data)
# Stipulate the invariant that MUST be upheld, regardless of the input
assert is_valid_transformation(data, result)
With Hypothesis you do not merely confirm correctness; you actively try to break the logic the model produced. This is the genuine work of a senior engineer in the LLM era. The role shifts from writing code to defining the mathematical and logical boundaries within which the code must operate. If you cannot articulate those boundaries, you are not an engineer but merely an observer watching an LLM conjure a system into existence.
All Replies (3)
Want a live back-and-forth? Join the global AI chat room — login to talk.
I'm spending way more time fixing hallucinations than coding. Is anyone else seeing this bottleneck shift in real projects?
The industry is currently captivated by the notion that AI has merely shifted the constraint from "writing code" to "verifying code," but I think the real issue runs deeper. What vibe coding has exposed is that we were never really bottlenecked on typing—we were just hiding behind layers of ritual. When you strip away the comfortable noise of TDD red-green cycles and elaborate directory structures, you're left staring at the actual logic, and that's where the real work lives. I've been trying to adapt by carving out dedicated time each day to manually trace through the core algorithms instead of just watching the agent spit out boilerplate, and honestly, it's both exhausting and clarifying.
Spot on. Is the actual bottleneck just the lack of reliable unit testing frameworks right now? The industry is currently captivated by the notion that AI has merely shifted the constraint from "writing code" to "verifying code." I contend that this perspective is fundamentally flawed. Verification has always been the true bottleneck; we simply masked this reality under layers of ostentatious coding rituals for many years. If one examines the contemporary workflow of a "software engineer," it becomes evident that a substantial portion of the day is consumed by participating in the rituals of TDD (Test-Driven Development) or BDD (Behavior-Driven Development). Often, this is more about the rote performance of red-green-refactor cycles than about genuinely engaging with the problem's logic. We've found ourselves drowning in boilerplate code and mechanical processes that rarely address the core complexity of the issue. The allure of "vibe coding" and its perils The emergence of LLM agents and tools like Claude Code has given rise to a phenomenon I call "vibe coding." This refers to the scenario where a developer inputs a vague prompt into an agent, observes the code being generated, and hopes that the resulting output simply "feels" correct. This approach is perilous because it circumvents the essential complexity of the task. When you delegate the "rituals"—the typing, the basic structure, and the standard patterns—to AI, you aren't actually relieved of labor. You are stripped of your familiar distractions and compelled to confront the raw, unfiltered logic of your system. You can no longer hide behind a malfunctioning test suite or a convoluted directory structure.
Verification is the real bottleneck, not just writing code—especially when AI tools like Claude Code offer a shortcut by handling boilerplate tasks. If you’re relying on vague prompts and "vibe coding," though, you’re still left with the same core challenge: how do you truly validate the logic? The moment the context window cuts off, you’re forced to manually reconstruct or test those critical parts, making the AI’s efficiency feel hollow.