All posts

5 min readAi

Why Your AI Agent Writes Bad Code (and How AGENTS.md Fixes It)

Keep AGENTS.md small, move the details into docs, and save one-off jobs for skills. Your AI agent gets less noise and writes better code.

  • #ai
  • #agents-md
  • #claude-code
  • #skills
  • #developer-tools

Why Your AI Agent Writes Bad Code (and How AGENTS.md Fixes It)

You tweak your AGENTS.md, you install a few skills, and your AI agent still makes the same mistakes. Most of the time the problem isn't the model. It's what we feed it.

This post is my take on Kyle's video on Web Dev Simplified. These are the ideas that stuck with me, explained the simple way.

What the agent actually reads

When you send a prompt, the agent doesn't only see your prompt. It gets a whole bundle:

  • The system prompt. The tool (Claude Code, Codex, Pi…) writes this. You usually can't change it, so don't worry about it.
  • AGENTS.md. Your project's instructions. Claude Code calls it CLAUDE.md, Gemini calls it GEMINI.md. It's the same idea with a different name.
  • Skills. The name and description of every skill you've installed.
  • Docs and code. Any files the agent opens while it works.
  • Your prompt. The thing you actually typed.

The one to remember: AGENTS.md is sent in full, with every single request. If it's bad, every answer starts from a bad place.

When AGENTS.md goes wrong

Most big AGENTS.md files grow the same way. The agent makes a mistake, someone adds a bullet. Another mistake, another bullet. A year later, it's a 500-line list.

That's a problem because:

  • Every rule is sent every time. Ask for a small CSS fix, and the agent still reads your testing, code review and release rules.
  • It costs tokens. One popular open-source repo has an AGENTS.md of about 8,600 tokens. That's before you've typed a word.
  • Quality drops early. Context windows are huge now, but answers usually get worse well before they're full, somewhere around 100k–200k tokens. A big AGENTS.md eats a real share of that.

Even big companies' repos get this wrong. Some repeat the same section twice. Some keep a tiny AGENTS.md that just points to one giant file, which is the same problem in a different place.

Step 1: Keep AGENTS.md small

A good AGENTS.md only holds what matters for almost every task:

  • One or two lines about what the project is
  • Anything unusual, like "we use pnpm, not npm"
  • A rough folder layout
  • Links to the detailed docs, and when to read each one
# Acme Shop

An online store. Next.js frontend, Node.js API, one monorepo.

- Package manager: pnpm (never npm or yarn)
- `pnpm check` runs lint, types and tests together

## Layout

- `apps/web`: the store frontend
- `apps/api`: the REST API
- `packages/ui`: shared React components

## Read these when you need them

- Styling: `docs/css-conventions.md`
- TypeScript: `docs/typescript-conventions.md`
- Tests: `docs/testing.md`
- Opening a PR: `docs/delivery.md`

That's it. Notice what's missing: no "getting started" or setup section. The agent is working in a project that's already set up. It doesn't need install steps.

Step 2: Move the details into docs

When you catch yourself writing a whole section for one kind of work, give it its own file in docs/:

docs/
├── css-conventions.md
├── typescript-conventions.md
├── testing.md
└── delivery.md

Each file is just a plain list of rules:

# CSS conventions

- Use Tailwind classes. No new `.css` files.
- Colors come from the tokens in `styles/globals.css`.
- Every new component must work in dark mode.

Now the agent opens css-conventions.md only when it's styling something. The rest of the time, those lines don't cost anything.

Docs can link to each other too. For example, typescript-conventions.md can say "for test files, see testing.md." Humans can read them as well, so they double as normal project docs.

Things like "run lint and Prettier before a PR" belong in delivery.md, not in AGENTS.md. You're not opening a PR on every request.

Step 3: Grow it from real mistakes

Don't ask the agent to "read my repo and write an AGENTS.md". You'll get a long, wordy file full of things you never needed.

Start with the few lines from Step 1. Then:

  1. Work with the agent normally.
  2. When it does something you don't like, write a rule for it.
  3. Put the rule in the right doc, not in AGENTS.md.

Don't try to guess mistakes in advance. Fix the ones you actually see.

Step 4: One file for every tool

If your team uses Claude Code and other agents, you need both CLAUDE.md and AGENTS.md.

The easy way is a CLAUDE.md that just says "read AGENTS.md". But agents add rules to whichever file they're looking at, so the two files slowly drift apart.

A better way is a symlink. CLAUDE.md becomes a shortcut to AGENTS.md, so there's only ever one real file.

On Mac or Linux, write the real file first, then the link name:

ln -s AGENTS.md CLAUDE.md

Check that it worked:

ls -l CLAUDE.md
# CLAUDE.md -> AGENTS.md

On Windows, the order is flipped: link name first, then the real file. Run it in Command Prompt as Administrator:

mklink CLAUDE.md AGENTS.md

Now an edit to either name changes the same file.

Step 5: Nested files for monorepos

In a monorepo, the frontend and backend often have very different rules. Maybe the web app is TypeScript and the API is Python. Putting both in one file means the agent reads both every time.

Instead, put an AGENTS.md inside each app:

AGENTS.md              ← rules for the whole repo
apps/
├── web/
│   └── AGENTS.md      ← React and CSS rules
└── api/
    └── AGENTS.md      ← API and database rules

The root file keeps the shared stuff, like the layout and the package manager. Each app's file has its own rules, plus a line about what that app does.

Which files get loaded depends on where you start the agent. Start it in apps/api, and it reads both the API file and the root file. Start it at the root, and it may only read the root one.

Step 6: Skills for one-off jobs

A skill is a folder with a SKILL.md file inside. It teaches the agent how to do one specific job.

You can install skills other people wrote. skills.sh is a good place to find them:

npx skills add <skill-repo-url>

The top of every SKILL.md has a name and a description:

---
name: auth
description: How login and sessions work in this app. Use when
  touching sign-in, sign-up or protected routes.
---

# Auth

- Sessions live in a signed cookie called `sid`.
- ...

Here's how it works. The agent always sees the name and description of every skill. It only reads the full file when the task matches. So a skill about auth costs almost nothing until you actually work on auth.

You can also call a skill yourself. In most agents, type / and the skill name, then your prompt.

Docs or a skill?

Ask one question: does this apply to most tasks, or just one kind of task?

It's…Put it inExample
General, used almost alwaysAGENTS.md, or a doc it links toTypeScript style, folder layout
Specific, used now and thenA skillHow auth works, a release script

Together, you get a tiny AGENTS.md that points to a big web of docs and skills. The agent pulls in only what the task needs.

Things to watch out for

  • Don't list exact file paths. Files move. A stale path in AGENTS.md confuses the agent more than no path at all. Keep the layout high level.
  • Don't repeat yourself. One rule, one place. If two docs say the same thing, they'll drift apart.
  • Skills aren't free. Every installed skill adds its description to every request. Remove the ones you don't use.
  • Windows symlinks need rights. Run mklink as Administrator, or turn on Developer Mode.
  • Tools differ. Not every agent loads nested AGENTS.md files the same way. Check your tool's docs.

That's it

Keep AGENTS.md short and general. Move the details into docs, save the one-off jobs for skills, and only add a rule after the agent gets something wrong. Less noise in, better code out.

Share this post