← all posts
·7 min read·by Dru Edwards·#ai #agentic-engineering #verification #craft

Stop Letting Your Agents Write Slop

If your AI agents keep writing bad code, the model usually isn't the problem — the system around it is. The fix is a small set of guardrails that take trust out of the loop.

If your agents keep writing bad code, it's tempting to blame the model. Almost always, you'd be blaming the wrong thing. The model is doing its job. The system you put around it isn't.

Here's the answer up front: LLM code isn't inherently slop. If you're getting slop, the problem is the engineering around the agent, not the model that wrote the output. Fix the system and the slop stops.

Spend any time on AI Twitter and you'll see the same complaint on a loop: "AI just writes slop." And honestly, the code in the screenshots usually is sloppy — no type hints, no tests, error handling that wasn't really thought through, abstractions that smell wrong.

But same models, different setups, different outcomes. The people getting durable results aren't running a secret model. They built something around it. A short breakdown of the pattern lays it out plainly, and it lines up with what I keep relearning on my own hardware: the agents that produce code worth keeping are the ones with less room to produce slop that survives.

Why this matters

If you run one agent now and then, the slop tax is manageable. You read every output, you toss the bad ones, you move on. That doesn't scale.

The second you've got several agents running in parallel, eyeballing everything by hand falls apart. You can't read every file they touch in a day. So you've got two choices: build a system that catches bad output before it lands, or accept that running more agents just means producing more debt faster. There's no third option, and pretending there is one is how the debt sneaks up on you.

Rule zero: never patch bad output

This is the load-bearing rule, and it's the one most people skip.

When an agent hands you something wrong, the instinct is to fire off a follow-up — "actually, change line 23 to..." — and patch it in place. Don't. Bad output isn't a starting point. It's a signal that something upstream was wrong: the prompt, the context, or the spec.

So diagnose the root cause, reset the run, and re-run from scratch with the inputs fixed. Patching after the fact quietly teaches you — and the agent — that wrong is an acceptable first draft. It isn't. Fix the upstream thing and let it generate fresh.

Takeaway: patched bad output ages worse than fresh good output. Every time, no exception.

The six guardrails

Strip away the talk and the setups that work all converge on the same handful of guardrails. Here they are.

1. Hooks — the first line of defense. Pre-commit hooks that run linting, type checks, and tests before any agent change can be committed. If the hooks fail, the run resets. That one move turns the agent from a thing that writes code into a thing that has to pass checks before its code counts.

2. Quality gates — set the bar high. Linting and type checking at the strictest reasonable settings. Coverage thresholds with a 100% pass rate to advance. The model rises to the standard you set. Set a low one and you get low output — it's that direct.

3. Anti-mock testing. Tests that run the real code paths, not mocked dependencies. Agents love to mock, because mocking makes tests go green and feels like proof. It isn't. Insist on integration-style tests where the dependencies are real or close to it.

4. Standardization — one place for everything. Every agent writes to a known directory structure. Naming conventions. Logs in one format. The "wait, where did this file even come from?" chaos disappears the moment there's only one place agents are allowed to put things.

5. Per-agent isolation. Each agent gets its own worktree, branch, or clone. They can't overwrite each other. Cross-agent file access stays denied unless you grant it on purpose. When something goes sideways, you can point at which agent did it.

6. Hard blocks on dangerous commands. Some commands are off-limits — git push origin main, anything with --force, anything destructive against production. Block them at the runtime layer. Don't trust the agent to read the rules and behave. Enforce them.

And underneath all six, quietly doing the heavy lifting: traceability. Every change, every command, every output logged with agent ID, timestamp, and the task it was on. When a regression shows up two weeks later, you trace it back to the exact run that introduced it. Skip this and you're guessing in the dark.

Takeaway: the guardrails work because they pull "trust" out of the loop. The agent doesn't get trusted — its output gets verified, automatically, every time.

From my own bench

I do almost all my real building with agents in the loop now — voice assistants, retrieval agents, multi-agent setups running on my own hardware. So this isn't theory I read somewhere. It's the order I actually learned these guardrails, roughly worst-to-first by how long it took me to give in:

  • Pre-commit hooks for lint and type check. First thing I do on every project now. Cheap, and it pays immediately.
  • Worktree-per-agent for parallel runs. Took me longer to learn. I kept letting agents share a directory and kept generating merge conflicts I had to untangle by hand.
  • Hard blocks on destructive commands. Added the day after an agent ran git reset --hard on the wrong branch. The fix was one line. The cleanup wasn't.
  • Anti-mock testing as the default. Still not where I want it. Agents really, really want to mock things, and I have to keep pushing back.
  • Traceability logs. The boring one I always under-invest in until I need them to figure out what broke.

The pattern is hard to miss once you see it: every guardrail I added was one I wish I'd had before the thing that taught me to add it. The slop didn't drop because I got better at prompting. It dropped because the agents had less room to ship slop that survived.

Try it today

StepWhat you doWhy it pays off
1. Add a pre-commit hook with lint + type check + testsEven a bare-bones version. If any check fails, the commit aborts.Slop dies at the commit boundary instead of inside your codebase. An hour to set up, weeks saved downstream.
2. Give every agent its own worktreeOne agent per branch, or one per clone. Never two agents in the same directory.Parallel runs stop stepping on each other, and when something breaks you can isolate which agent did it.
3. Pick one dangerous command and hard-block itgit push origin main. rm -rf. DROP TABLE. Block it at the runtime layer, not in instructions.Instructions get ignored. Hard blocks don't. One blocked command is one incident you never have to clean up.

Where people get burned

  • Patching bad output instead of resetting. Every patch makes the next bug harder to trace. Fix: if it's wrong, reset and re-run. Treat resets as cheap, because they are.
  • Loose gates because "the agent can't pass them yet." Then it never will have to. Fix: set the gates to the standard you'd hold your own shipped work to, and let the agent rise to meet it.
  • Letting agents mock dependencies. Tests that look green and lie. Fix: prohibit mocks at the policy level and make integration-style testing the default.
  • Skipping traceability because nothing's broken yet. When something does break, you'll wish you'd had it. Fix: log everything, store it cheap, query it when you need it.
  • Trusting the agent's instructions to keep things safe. Models drift, ignore, and hallucinate. Fix: if it's dangerous, block it at runtime, not in the prompt.

A tool, and a question worth sitting with

  • A tool to try: pre-commit — the framework, not just the concept. One config file, and the same hooks run for everyone touching the repo. Pair it with worktrees for the isolation piece.
  • A watch worth your time: the breakdown that names the pattern is about twelve minutes and concrete about each guardrail. Pairs well with my last post on the spec-driven setup.
  • A question to actually sit with: which of your agents could run git push origin main right now if it decided to? If the answer is anything above zero, that's a runtime hard-block away from being a real problem.

The bottom line

The setups that run agents well aren't winning on better prompts. They're winning because they built something where bad output literally can't survive — hooks block it, gates reject it, isolation contains the blast radius, traceability finds the source.

The model isn't the bottleneck. The system around it is. Build the system you'd build for a fast, eager helper that occasionally tries to push straight to main — and most of the slop problem solves itself.

— Dru Edwards