Your AI Coding Agent Isn’t Broken. Your Setup Is.
You remember the moment it clicked. You opened Claude Code, described an app you’d been thinking about for weeks, and watched it materialize. Routes, database models, a clean React frontend — all from plain English. You shipped a working prototype in a weekend. You told your friends. You thought: this changes everything.
Three weeks later, you’re staring at a diff that makes no sense. The agent just rewrote your authentication middleware — the same middleware it wrote two days ago — and broke the permissions system in the process. You ask it to fix the permissions, and it does, but now three API endpoints return 500 errors. You roll back, re-explain the architecture, and ask it to try again. It apologizes, says it understands, and produces code with the exact same problem.
You’ve been at this for two hours. You’re not building anymore. You’re babysitting.
If this sounds familiar, welcome to the club. You’ve hit what I call the vibe coding wall — the point where the practice that felt like a superpower starts feeling like a liability. And I want to make a case that might surprise you: the problem isn’t your agent. It’s everything around your agent. Fix the environment, and the magic comes back — stronger and more reliable than the honeymoon phase ever was.
The Honeymoon Is Real (and That’s Part of the Trap)
Let’s be clear about something: vibe coding genuinely works. The first few hundred lines of any project are legitimately magical. You describe a feature, the agent writes it, you run it, it works. You push back on a design choice — “actually, use JWT tokens instead of sessions” — and the agent refactors smoothly. You’re moving faster than you’ve ever moved.
And the code isn’t bad. It’s organized, it follows conventions, it handles edge cases you didn’t even mention. For the first 500 to 1,000 lines, everything hums.
The reasons are straightforward. Your entire project fits in the agent’s context window — it can “see” every file, every function, every relationship. There are no contradictions yet because there’s only one way to do things — the way the agent just set them up. The blast radius of any mistake is tiny. You and the agent share the same mental model because the project is simple enough to have exactly one mental model.
This is the honeymoon phase, and it’s real. For prototypes, internal tools, hackathon entries, and personal projects, vibe coding is legitimately faster than writing code by hand. Nobody is going to talk you out of that.
The trap is thinking this will keep working as the project grows. It won’t. And it’s not because of anything you’re doing wrong.
What Happens at 3,000 Lines
Somewhere between 2,000 and 5,000 lines of code, the tide comes in. You don’t notice until you’re ankle-deep.
The first sign is usually repetition. You ask the agent to add a new API endpoint, and it creates a helper function that already exists in another file. Not a copy — a slightly different version, with a slightly different name, doing almost the same thing. You catch it, point it out. The agent apologizes and refactors. Next session, it happens again.
Then comes contradiction. The agent starts making architectural choices that conflict with decisions it made earlier. It introduced a repository pattern for database access in week one, but now it’s writing raw SQL queries directly in route handlers. It’s not being lazy — it genuinely doesn’t remember making those earlier decisions.
Next is what I call phantom completions. You ask the agent to implement a feature. It writes the code, tells you it’s done, and the code looks reasonable. You run it. It doesn’t work. The function signature doesn’t match what the caller expects. Or it imports a module that doesn’t exist. Or it handles the happy path but crashes on every edge case. The agent is confident. The code is broken.
And then the worst one: the cascade. You ask the agent to fix a bug. The fix introduces a new bug. You ask it to fix that bug, and it reverts the first fix. Each fix creates new problems because the agent can’t see the full picture anymore.
Here’s what a typical day looks like once you’ve hit the wall:
- You spend 15 minutes explaining the project state at the start of each session
- You ask for a feature and get code that contradicts existing patterns
- You correct the agent, and it over-corrects, changing things that were working
- You discover that a feature from last week is quietly broken — the agent modified it as a side effect of something else
- You start writing increasingly long, detailed prompts trying to constrain the agent’s behavior
- You wonder if you’d be faster just writing the code yourself
That last thought is the wall. And virtually everyone who vibe codes past a certain project size hits it.
The Eight Ways It Falls Apart
I’ve watched enough projects hit this wall to identify eight specific, predictable failure modes. They’re worth naming, because naming a problem is the first step to fixing it.
Context overflow. Your project grows beyond what the agent can hold in its context window at once. It starts inferring function signatures from training knowledge instead of reading your actual code. It calls checkPermission(user, resource) with two arguments when your module expects three: checkPermission(user, resource, action). Close, but wrong. And the agent has no idea it’s guessing — there’s no warning, no “I can’t see enough of your project to be confident.” Just plausible, subtly broken code.
Convention drift. File 1 through 30 use camelCase. File 40 uses snake_case. File 52 uses callbacks instead of async/await. File 58 creates a helpers/ folder that duplicates the purpose of services/. By file 70, your codebase looks like it was written by five developers who never talked to each other. The agent infers conventions from whatever code happens to be in context — and as the project grows, the local context stops being representative of the whole.
The copy-paste explosion. Search your codebase for “format.” How many date formatting functions do you have? If you’ve been vibe coding for more than a few weeks, the answer is probably more than one. Maybe more than three. Each one with a different name, slightly different implementation, and at least one of them has a subtle bug. Studies have measured AI-generated code at 8x the duplication rate of human-written code. That’s not a minor difference — that’s a fundamentally different kind of codebase, growing faster in volume than in capability.
Phantom completions. The agent says “Done! I’ve implemented the user search feature.” You run it. The search bar throws a JavaScript error because a state variable was never initialized. You fix that. The API call fails because the endpoint URL doesn’t match. You fix that. The query returns empty because it’s searching for exact matches instead of using wildcards. Three individually small bugs that together mean the feature doesn’t work at all — and the agent reported it as complete.
The fixation loop. You report a bug in the discount calculation. The agent fixes the discount but breaks the tax calculation. You report the tax problem. It fixes the tax but reintroduces the discount bug. Three rounds in, you’re no closer to a working function than when you started. Each fix looks like progress. None of it is.
Architecture erosion. In week one, you had clean layers — routes call services, services call repositories, repositories touch the database. By month two, route handlers are full of raw SQL queries and business logic. The agent optimized for “get it done” and took shortcuts through your architectural boundaries. Each violation makes the next violation more likely because the agent sees the shortcuts as established patterns.
The blank slate problem. Every Monday morning, you open a new session and the agent has no idea what you’ve been building. It doesn’t know you switched from SQLite to PostgreSQL. It doesn’t know about the migration you ran Thursday. It doesn’t know about the bug that took an hour to fix or the workaround for the third-party API that returns inconsistent timestamps. You re-explain, but you can’t possibly re-convey every decision. The agent fills in the gaps with assumptions — and some of those assumptions are wrong.
Confidence without verification. You ask for rate limiting. The agent writes an in-memory implementation and says “Done!” But your app runs behind a load balancer with three instances, each with its own memory. A user can make 300 requests before hitting any limit. The rate limiter is architecturally broken for your deployment, and the agent presented it with the same certainty it uses for renaming a variable.
The Pattern Underneath
Read through those eight failure modes again and you’ll see it: they’re all the same problem wearing different hats.
The agent can’t see enough of the project. The agent can’t remember the project’s rules. The agent can’t find what already exists. The agent can’t verify its own work. The agent can’t see the consequences of its changes. The agent can’t sense structural boundaries. The agent can’t remember anything between sessions. The agent can’t distinguish what it knows from what it’s guessing.
Every one of these is a missing capability — something the agent can’t do, not because it’s broken, but because it was never given the infrastructure to do it.
Think about it like onboarding a new hire. When a brilliant new developer joins your team and produces inconsistent code, you don’t conclude they’re a bad programmer. You look at what’s around them. Is there a style guide? Have they been shown the existing utilities? Is there a CI pipeline catching regressions? Are architectural conventions documented? If the answer to all of those is “no,” the inconsistency isn’t their fault. It’s an organizational failure.
Your agent is that new hire. It showed up on day one, enormously capable, and you pointed it at a blank directory with no documentation, no tests, no linting rules, no architectural constraints, and no memory of previous sessions. Then you were surprised when the output was inconsistent.
The agent is not broken. It’s unsupported.
Why “Try Harder” Doesn’t Work
The natural response to hitting the wall is to try harder at communicating.
You write longer prompts. You paste in more context. You start each session with a multi-paragraph briefing about the project’s architecture. You create “rules” at the top of your prompts: “Always use the repository pattern. Never write raw SQL. Follow the existing naming conventions.”
This helps. Briefly. Then the prompts get so long that the agent spends more of its context window reading your instructions and less of it looking at the actual code. You’re caught in a trap: the more context you add for the agent, the less room the agent has to do its work.
Some people start over. Each time the project gets tangled, they create a fresh session and re-describe everything from scratch. This works for a while because a fresh context means a fresh agent with no confused state. But you’re paying a tax every time — rebuilding context the agent should already have.
Others go to micro-management. Instead of asking for features, they dictate specific code changes: “In app/routes/users.py, add a new route at line 47 that calls UserRepository.find_by_email() and returns a 404 if the user doesn’t exist.” This reduces errors, but it defeats the purpose. You’re doing all the thinking and using the agent as a slow, expensive keyboard.
All of these strategies share the same flaw: they treat the problem as a communication problem. If the agent is making mistakes, it must be because you aren’t explaining things clearly enough. Find the right words, write the right prompt, provide the right context — and the agent will get it right.
But the problem isn’t communication. The problem is architecture.
The Three Eras of Working With AI
To understand where the real fix lives, it helps to see how the field has evolved. It’s happened in three stages, each one solving real problems and each one hitting a ceiling.
Era 1: Prompt Engineering (2023-2024). The question was “what should I tell the model?” You crafted careful instructions — role assignment, chain-of-thought reasoning, step-by-step breakdowns. This worked brilliantly for single-turn interactions. One function, one script, one self-contained piece of code. But it broke down for projects. Each prompt was independent, so nothing enforced consistency between file A and file B.
The instinct this era created is powerful and misleading: if the output is wrong, write a better prompt. This reflex still drives most people’s troubleshooting. It’s like trying to prevent car accidents by writing better road signs — useful, but no substitute for guardrails and lane markings.
Era 2: Context Engineering (2025). The question shifted to “what should the model see?” Instead of just optimizing instructions, people started curating the entire input window — system prompts, retrieved documentation, tool definitions, memory files. The insight was simple but profound: a model can only reason over what it can see. If it produces bad output, look at what was (or wasn’t) in the context window.
This was a genuine leap forward. Agents could follow project-specific patterns, use existing utilities, and maintain consistency within a session. But context engineering hit its own ceiling: you could show the agent your entire architecture, your naming conventions, your existing utilities, your test patterns — and it could still ignore conventions it had clearly seen, skip tests it knew about, and violate architecture it understood.
Knowing is not the same as doing. Information without enforcement is just a suggestion.
Era 3: Harness Engineering (2026). The question became “how should the whole system be designed?” The realization was that the agent is just one component in a larger system — and often not the most important one. Harness engineering is about designing everything around the agent: what it can do, what it can’t do, how it verifies its work, how it remembers across sessions, how it gets feedback, how it’s constrained from making certain classes of errors.
Each era subsumes the previous one. You still write good prompts (Era 1). You still curate good context (Era 2). But now you also build the infrastructure — tests, linters, architectural constraints, quality gates, memory systems — that makes those prompts and that context produce reliable results at scale.
Here’s the clearest way to see the difference: In Era 1, if the agent writes buggy code, you rewrite the prompt. In Era 2, you add more relevant code and documentation to the context. In Era 3, you add a test that catches that category of bug automatically, so it can never slip through again — regardless of the prompt, regardless of the context.
Era 1 fixes the current interaction. Era 2 improves the current session. Era 3 improves every future session.
The Agent Is Not the Hard Part
There’s a comforting belief that lives in the back of every vibe coder’s mind: the next model upgrade will fix everything.
It won’t. And if you keep waiting for it, you’ll spend the next two years sitting on a solution that already exists.
Modern frontier models score 90%+ on standard coding benchmarks. They can implement complex algorithms, refactor tangled code, reason about concurrency, and write idiomatic code across dozens of languages. These models are genuinely, remarkably capable. You’ve seen it yourself in the honeymoon phase.
So why do they fail so reliably in real projects?
Because they’re operating without support. The raw capability is there. What’s missing is persistent memory, architectural constraints, verification mechanisms, curated context, and governance. These aren’t things better training will fix. They’re structural gaps that exist by design.
The evidence is unambiguous. The LangChain team improved their coding agent’s performance on a standardized benchmark by 13.7 points — jumping from the top 30 to the top 5 on the leaderboard — without changing the model. Same weights, same capabilities. They changed the harness: the tools, constraints, execution environment, and feedback loops. The improvement came entirely from what surrounded the model.
Vercel’s AI team had an agent with 15 specialized tools running at 80% accuracy. Their fix was counterintuitive: they removed tools, stripping down to 2 general-purpose ones. Accuracy went to 100%. Token usage dropped 37%. Speed improved 3.5x. Fewer options, better results.
And the APEX-Agents benchmark tested frontier models on realistic professional tasks — multi-file changes, ambiguous requirements, existing codebases. The same models scoring 90%+ on standard tests achieved about a 24% pass rate. The failures weren’t reasoning failures. The models didn’t produce wrong algorithms. The failures were orchestration problems — lost context, retry loops, inability to coordinate across files. Harness failures, not model failures.
The pattern is clear: once a model crosses the capability threshold, additional model intelligence has diminishing returns. Harness quality has increasing returns. Every improvement to the harness compounds — a better documentation file improves every session, a test suite catches errors across all features, an architectural constraint prevents an entire class of bugs.
This is why the teams shipping thousands of commits per month aren’t using secret, more powerful models. They’re using the same models available to everyone. The difference is the harness.
Constraints Make Agents Better
This is the single most counterintuitive and most important idea in this entire discussion, so let me say it plainly: constraints improve agent performance.
Not “constraints are a necessary trade-off.” Constraints actively make the agent produce better results.
Your instinct when working with an agent is to give it more — more freedom to choose its approach, more context, more tools, more flexibility. The agent is smart, so let it use that intelligence by giving it maximum room to operate.
The evidence says the opposite.
When an agent can do anything, it has to decide what to do. Every decision costs tokens. The agent reasons about options, evaluates trade-offs, picks a path. Sometimes it picks wrong and backtracks. The more options available, the more computation goes to navigating the decision space instead of solving the actual problem.
When the harness defines clear boundaries, the decision space shrinks. The agent doesn’t waste tokens deciding which architectural pattern to follow — there’s only one option, enforced by linter rules. It doesn’t waste tokens choosing between fifteen tools — there are two, and the right one is obvious. It doesn’t wonder whether to refactor that utility module — the governance rules say it’s out of scope.
Constraints eliminate dead ends before the agent can explore them.
This is why rigid architecture rules improve agent output. When the agent knows that all database access goes through the repository layer (because a linter rejects anything else), it doesn’t spend time considering inline queries. When a module boundary is enforced by the build system, the agent doesn’t explore designs that violate it. The constraint isn’t taking away a good option — it’s eliminating a hundred bad options the agent would otherwise evaluate and reject one by one.
The best harnesses feel restrictive to humans but liberating to agents. Humans chafe at constraints because they have judgment for navigating open-ended situations. Agents thrive under constraints because constraints transform open-ended situations into tractable ones.
So What’s the Fix?
The fix has a name: harness engineering. It’s the discipline of building the structured environment that makes an AI coding agent reliable at scale.
The metaphor comes from horsemanship. A horse is powerful — a thousand pounds of muscle that can cover ground faster than any human. But a horse without a harness goes where it wants, stops when it’s tired, and spooks at shadows. Put that same horse in a harness and suddenly all that power is channeled. You’re not reducing the horse’s capability. You’re making it useful.
The harness is everything you build around the model:
Documentation and memory. AGENTS.md files that tell the agent how your project works, what patterns to follow, and what mistakes to avoid — read automatically at the start of every session. Architecture decision records that capture why things are the way they are. Progress files that track what’s been done.
Architecture as guardrails. Module boundaries enforced by linter rules and build configurations, not just conventions. Dependency directions that the agent literally cannot violate without triggering a failure. When the architecture is defined in code rather than in your head, the agent can’t erode it.
Automated verification (back-pressure). Tests, type checkers, linters, and formatters that run after every agent change and provide feedback before work is marked complete. The agent doesn’t get to decide whether its code is correct. The quality gates decide.
Context management. Controlling what enters the agent’s context window and when, so it always has the right information for the task at hand. Progressive disclosure instead of dumping everything at once.
Governance. Boundaries on what the agent can and can’t do. Which files are off-limits. Which operations require approval. When to stop.
All of this comes together in a simple formula: Agent = Model + Harness. The model provides intelligence. The harness provides everything else — constraints, tools, memory, verification, context curation, governance.
If you’re not the model, you’re the harness.
What Working This Way Actually Looks Like
Here’s a realistic day in the life of someone who’s made the shift.
Morning. You open your laptop and find three PRs from overnight agent runs. Two are clean — tests pass, code follows patterns, changes are scoped correctly. You review and merge them. The third put validation logic in a route handler instead of the validation layer. The old you would fix the code. The new you adds a linter rule that flags validation logic outside the validation layer, updates your documentation, and kicks off a new agent session. The code comes back correct this time — because the environment now makes the mistake structurally difficult to repeat.
Fixing the code fixes one PR. Fixing the harness fixes every future PR.
Mid-morning. You write task specifications for four features. Not vague descriptions (“add user profile editing”) but precise specs: which endpoint, what fields, how to validate them, which pattern to follow, which existing file to use as a template, what tests to write. This is the new “coding” — expressing intent with enough precision that the implementation follows naturally.
Afternoon. You launch four agent sessions in parallel, one per feature. You monitor progress, catch one agent stuck in a fixation loop, diagnose the cause (it didn’t know about an existing utility function), add the utility to your documentation, restart the session, and it finishes cleanly.
End of day. Eight meaningful changes merged. Four features, two bug fixes, a refactor, and a cleanup. Your harness is slightly better than this morning — two new linter rules, updated documentation, a refined detection pattern. You didn’t write a line of application code. But you did a lot of engineering.
The Skills That Shift
This way of working doesn’t erase your existing skills. It re-weights them.
Still critical: Understanding code (you can’t review what you don’t understand). Architecture and systems thinking (now more important than ever, because architecture is agent guidance). Debugging (both the code and the agent’s behavior). Domain knowledge (the one thing no model can provide for itself).
Newly critical: Specification writing — articulating intent precisely enough for an agent to execute it correctly. Constraint design — deciding what the agent can’t do, which is as important as deciding what it can. Feedback loop design — building mechanisms where agent errors automatically generate permanent improvements.
Less critical: Typing speed. Memorizing API signatures. Writing boilerplate. The agent handles these better than you do. Your job is knowing when and why, not the exact parameter order.
The biggest mindset shift is moving from “fix the bug” to “fix the system that allowed the bug.” When an agent puts validation in the wrong layer, the amateur moves the code. The harness engineer adds a linter rule and updates the documentation. The five-minute fix helps once. The twenty-minute fix helps forever.
The Emotional Part Nobody Talks About
Let’s be honest about feelings for a minute.
It can feel weird to ship code you didn’t write. You open a PR, it’s 500 lines of well-structured code that passes all the tests, and your contribution was a six-sentence specification and a twenty-minute review. Is this your work?
Yes. A film director doesn’t operate the camera, doesn’t act the scenes, doesn’t compose the score — but the film is their work. The harness engineer designs the system, specifies the intent, reviews the output, and maintains the standards. The code is your work.
It can feel like cheating, especially if you work with engineers who still write everything by hand. Get over this one quickly, because it will hold you back. Engineering has always been about outcomes, not keystrokes.
And it can feel threatening. “If the agent writes all the code, why do they need me?” The answer is the thesis of everything above: the harness IS the engineering. The agent without a harness produces inconsistent, unreliable, architecturally incoherent code. The harness is what makes the agent’s output production-worthy. And the harness doesn’t design itself.
The engineers who should be worried aren’t the ones learning to build harnesses. They’re the ones who refuse to adapt — who insist on writing every line by hand while the engineer next to them merges ten PRs before lunch.
What To Do Right Now
You don’t need to build a perfect harness before your next session. Start with the highest-leverage moves:
- Write down what’s in your head. Spend 30 minutes documenting everything your agent needs to know — architectural decisions, naming conventions, patterns, things you’ve tried and rejected, dependencies between components. This is the seed of your
AGENTS.mdfile. Put it where your agent reads it automatically. - Add one linter rule. Pick the convention your agent violates most often. Make it mechanically enforced instead of advisory. The first time the linter catches a violation before you do, you’ll feel the difference.
- Write one test. For the module you’re most actively developing, write a test that catches the most common phantom completion — the thing that looks done but isn’t. Now the agent has to actually get it right, not just generate plausible code.
- Audit your correction ratio. In your next session, tally how many prompts are “build something new” versus “fix something the agent got wrong” or “re-explain something it should already know.” If more than a third of your prompts are corrections, you’re past the wall. The things you keep re-explaining are the things that need to live in documentation.
- Reframe your frustrations. The next time your agent generates inconsistent code, don’t think “the model isn’t good enough.” Think “what’s missing from the environment that would prevent this?” Don’t write a longer prompt — add a linter rule. Don’t blame the model’s reasoning — add tests that catch the regression.
The Wall Is a Doorway
The vibe coding wall is real. But it’s not a dead end. It’s a signal that you’ve outgrown one way of working and you’re ready for a more powerful one.
The teams shipping thousands of commits per month aren’t better prompt writers. They’re better harness engineers. They’ve built environments where agents reliably produce production-quality code, session after session, at a pace no human team could match. They’re using the same models you have access to. The difference is what they’ve built around those models.
The model is already good enough. The bottleneck is everything around it. And everything around it is something you can build.
The question isn’t whether you’ll need a harness. It’s how long you’ll keep hitting the wall before you start building one.
This post covers the ideas from Part I of Harness Engineering for Vibe Coders. The book goes deep on the how — architecture design, AGENTS.md files, back-pressure mechanisms, context management, session lifecycle, quality gates, parallel agents, memory systems, and more. If the problem resonates, the solutions are waiting.
Leave a Reply