DEV Community

Mārtiņš Veiss
Mārtiņš Veiss

Posted on

Agents don't fail at writing code — they fail at everything around it

I've spent about six months running Claude Code agents against a codebase big enough to
hurt — several hundred open issues, agents committing daily, a CI pipeline that fails loudly
when they get it wrong.

Here's the thing nobody told me going in: the agents were fine at writing code. Genuinely
fine. Give one a well-scoped function and it comes back with something reasonable.

What they were bad at was everything around writing code. Deciding whether the thing
should be built at all. Noticing that the helper they were about to write already existed
sixty lines up. Checking whether the fix worked before announcing that it did. Those aren't
coding failures — they're process failures, and no amount of better prompting fixed them,
because the problem was never the prompt.

What fixed them was writing the process down.

Four failure modes, and what each one taught me

1. The second helper

An agent needs to format a duration. It writes formatDuration(). Your codebase already has
humanizeElapsed() in a utils file it never opened.

This is the most expensive failure mode I hit, because nothing about it looks like a
failure. The code works. Tests pass. Review approves it. You only find out six months later
when you change how durations are displayed and catch three of the four places.

Agents are strongly biased toward writing new code rather than finding existing code,
because writing is what they're rewarded for and searching is expensive. The counter-pressure
has to be explicit: before you write a helper, prove the concept doesn't already exist.
Not "check quickly" — prove it, with a search you can cite.

The same discipline kills the "v2" reflex, where an agent that finds a function it doesn't
fully understand writes processDataV2() next to it and leaves both in the tree forever.

2. "Done" that was never checked

Ask an agent whether the fix works and it will tell you the fix works. It is not lying. It
has genuinely reasoned its way to a confident answer, and reasoning feels like verification
from the inside.

The rule that fixed this is blunt: you may not say "done", "fixed", or "passing" without
pasting the command output that proves it.
Not a description of the output. The output.

The interesting part is how often an agent following that rule catches itself. It goes to
run the test so it can quote the result, the test fails, and the claim never gets made. The
verification step isn't a check on the answer — it's what produces the answer.

3. Fixing one of twelve

An agent fixes a null-check bug in UserService. The same bug is in eleven other services,
because they were all written from the same template by the same agent three weeks earlier.

Agents fix what you point at. They don't generalise from one instance to a class of
instances unless you ask, and the whole reason the bug is interesting is that it's probably
systemic. So after every fix: sweep the adjacent files for the same defect, and file what
you find.
File it even if you can't fix it now — an unfixed bug you've recorded is a
backlog item, an unfixed bug nobody noticed is a landmine.

4. Debugging by guessing

An agent hits a failing test and immediately proposes a fix. Then another. Then another. Each
one is plausible, none of them work, and forty minutes later the file has accumulated three
speculative changes and the original bug is still there — now harder to see.

What's missing is the step where you find out why it fails before deciding what to change.
Read the actual error. Reproduce it in isolation. Form one hypothesis, test that hypothesis,
and only then edit. And when three attempts have failed, stop — a fourth attempt is not a
strategy, it's a slot machine. Escalate with what you learned.

Why I put these in skills rather than CLAUDE.md

The obvious place for rules like these is your project instructions, and I started there. It
doesn't scale.

Everything in CLAUDE.md is in context permanently, competing with the actual task for
attention. A twelve-line debugging protocol is exactly right when a test is failing and pure
noise the other 95% of the time. Push enough process in there and the important rules get
diluted by the situational ones.

Skills load on trigger. The debugging discipline shows up when something breaks and stays out
of the way otherwise. That's the whole difference, and it turns out to matter a lot.

The second reason is portability. Process discipline isn't project-specific — the "second
helper" problem is the same in every codebase I've worked in. Keeping it in one project's
instructions means re-deriving it in the next project. Packaged as a plugin, it moves.

The set

I've extracted the general, project-agnostic half of what accumulated and published it as a
Claude Code plugin marketplace:

/plugin marketplace add mrveiss/Claude-Dev-Skills
/plugin install claude-dev-skills@claude-dev-skills
Enter fullscreen mode Exit fullscreen mode

Eight skills:

  • process — the approach discipline: explore before building, plan a multi-step change, debug methodically, verify before claiming, dispatch parallel agents, finish a branch.
  • canonical-coding — one implementation per concept. Failure mode 1.
  • commit — commit workflow with pre-flight checks, auto-format, and retry logic for when pre-commit hooks rewrite files underneath you.
  • review-lenses — review by domain lens (architecture, delivery, frontend, docs, UX, visual craft) instead of one undifferentiated "review this".
  • gap-audit — sweep adjacent files after a fix and file the gaps. Failure mode 3.
  • web-audit — security, SEO and AI-friendliness audit of a site: headers, DNS, TLS, CORS, email spoofing, exposed panels, per-page SEO, compromise indicators.
  • ui-design — visual direction, typography, colour, layout, spacing, motion, accessibility.
  • memory-cleanup — end-of-session memory hygiene, so context files stay an index instead of growing into sludge.

Apache-2.0, and two of them consolidate ideas from other skill authors — notably Jesse
Vincent's Superpowers suite, which is worth reading on
its own.

They came out of building AutoBot-AI. The
project-specific ones — the issue-to-merge loop, full-stack debugging, the codebase audits —
live in their own marketplace,
because they hardcode that platform's branch names and paths and are no use anywhere else.
This is the half that travels.

The part I'd push back on myself

Process discipline has a real cost. Every one of these rules makes the agent slower, and some
of them make it slower in ways that feel pointless in the moment — proving a helper doesn't
exist takes longer than writing the helper, almost every time.

The trade only pays off over a codebase's lifetime, which means it's genuinely the wrong call
for a prototype you'll throw away in a week. If you're spiking something, skip all of this
and let the agent write.

Where it pays is the second year, when you're the one maintaining what the agents built.


If you try the set, I'd like to know where it's wrong: does process route the way you
actually work, or does it fire when you don't want it? Is canonical-coding too strict for a
codebase that legitimately carries parallel implementations? And what's missing?

Repo: https://github.com/mrveiss/Claude-Dev-Skills

Top comments (0)