Back to AI guides

12 Rules for an Effective claude.md File

Stanley Ulili
Updated on August 18, 2026

The claude.md file (called agents.md in some other systems) is prepended to every prompt you send to your coding agent. It's a persistent constitution for how the agent should behave, what conventions it should follow, and what mistakes it should never repeat. A well-written one transforms an agent that constantly needs correction into one that operates reliably over long sessions with minimal oversight. A poorly written one wastes context on noise and still leaves the agent guessing.

These 12 rules cover the patterns that matter most: auto-improvement, testing discipline, safe dependencies, naming consistency, architecture orientation, and performance. They're written with Claude Code in mind but apply to any agent that reads a context file at session start.

Keep it under 500 lines

Before the specific rules, one overriding constraint. The claude.md is loaded into every session. LLM performance degrades as context grows, so a longer file doesn't mean better guidance — it means less reliable adherence to all of it.

A research paper screenshot highlighting the conclusion: "Our results reveal that models do not use their context uniformly; instead, their performance grows increasingly unreliable as input length grows."

Keep the file under 500 lines. If you have more than that, modularize: nested claude.md files in subdirectories (api/claude.md, web/claude.md) let you scope rules to specific parts of the project. Complex reusable workflows go in .claude/skills/ and get linked from the main file rather than inlined.

An animation showing a single `CLAUDE.md` file being split into multiple, more specific files for different parts of a project like `api/CLAUDE.md` and `web/CLAUDE.md`.

Rule 1: Auto self-improvement

Treat the claude.md as a failure log, not a wishlist. Every rule should exist because the agent made a specific mistake at least once. When that mistake gets corrected, it gets codified.

This framing, popularized by Mitchell Hashimoto (creator of Terraform), creates a feedback loop where the agent gets more reliable with every correction rather than repeating the same mistakes across sessions.

claude.md
## Keeping this file current

This file is a failure log, not a wishlist. Every line below exists because it went wrong at least once.

When you make a mistake, get corrected, or discover something about this codebase that wasn't written down:

1. Add one line to the failure log below, in the imperative, describing the correct behaviour.
2. Keep it specific to this repo. General advice belongs nowhere.
3. If the fix is a workflow rather than a rule, put it in `.claude/skills/` and link it from here.
4. Include the change in the same commit and mention it in your summary.

Keep this file under 500 lines. If a section outgrows its usefulness, move it to `api/CLAUDE.md` or a skill.

## Failure log

- Do not run `npm run dev` to verify a change; it never exits. Use `npm run check && npm test`.

A code editor displaying the "Keeping this file current" section within a `claude.md` file, demonstrating the self-improvement instructions.

Rule 2: A testing and linting loop

Define a strict loop the agent must complete before considering any task done. Without this, agents declare success based on whether the code looks plausible rather than whether it actually passes.

claude.md
## The loop

Every change runs through this. A task is not done until it is green.

```bash
npm run check      # tsc --noEmit, eslint, prettier --check
npm run test       # vitest, unit + integration
npm run test:api   # supertest against a throwaway Postgres
npm run db:migrate # never edit an applied migration, always add a new one
 

![A code snippet titled "The Loop" showing the specific `npm` and `xcodebuild` commands that constitute the testing and validation cycle.](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/0d5f17bc-ff5f-479a-816d-7eee958b8d00/public =1920x1080)

Pair the commands with behavioral rules: write the failing test first; run the loop after every meaningful edit; never report success on a red loop; explain why a test is wrong before changing it; never start long-running processes like `npm run dev` as the verification step.

## Rule 3: Ask before assuming

Ambiguity in a prompt causes the agent to guess. When the guess is wrong and the agent has already gone several steps deep, the cost of correction multiplies. One question upfront is always cheaper.

```markdown
[label claude.md]
## Ask before you assume

Never guess at intent. If a task leaves anything open — which screen, which endpoint,
what happens on failure, whether it needs a migration — stop and ask.

- Ask when the request could reasonably mean two different things.
- Ask before changing a public API shape, a DB schema, or anything in `packages/contracts/`.
- Do not invent product decisions, copy, or acceptance criteria.
- Do not widen scope past what was asked. Note the adjacent thing you spotted; don't fix it unprompted.
- If you had to assume something, list it at the top of your summary.

Rule 4: Strict type-checking

AI agents produce significantly fewer bugs when working in a strictly typed codebase. Type errors become a first-class signal rather than noise.

claude.md
## Type checking

Type errors are not warnings.

`tsconfig.json` — these stay on:
- `strict`: true
- `noImplicitAny`: true
- `noUncheckedIndexedAccess`: true
- `exactOptionalPropertyTypes`: true
- `noImplicitOverride`: true

No `any`. If a type is genuinely unknown, use `unknown` and narrow it.
Respect these from the start. Do not write loose code and correct it after the type check fails.

Rule 5: Well-maintained libraries only

Agents will install packages to solve problems that already exist in the standard library, or install packages from single maintainers with no recent activity. Both are risks: unnecessary maintenance burden in the first case, supply chain exposure in the second.

claude.md
## Dependencies

Code is cheap; maintenance isn't. Prefer a well-established package over rolling your own,
and prefer the platform over a package.

Before installing anything, check and state:
- Weekly downloads over ~100k, a release in the last six months, more than one maintainer.
- Nothing single-maintainer or freshly published for anything touching auth, crypto, networking, or file I/O.
- No new dependency for something the standard library or an existing dependency already does.

Ask before adding a dependency. Never add one as a side effect of another task,
and never pin to `latest` — exact versions only, lockfile committed.

A code editor snippet showing the "Dependencies" section, outlining the rules for selecting and installing external packages.

Rule 6: Naming conventions

Without a canonical vocabulary, agents will mix "login" and "sign in," or use deleteUser in one place and removeProduct in another. This makes the codebase harder to reason about for both humans and the agent itself.

claude.md
## Naming

Pick the existing word, don't coin a new one.

**Domain vocabulary:**

| Concept | Use | Never |
| :--- | :--- | :--- |
| A recorded walk | `hike` | trip, walk, activity, session |
| The GPS line | `track` | route, path, trace |
| A saved place | `waypoint` | pin, marker, poi |
| Account access | `Sign in` / `Sign out` | Login, Log In, Log out |

**Code:**
- Functions: `createHike`, `getHike`, `listHikes`, `updateHike`, `deleteHike`. Not `fetch`, `remove`, `save`, `handle`.
- Booleans read as assertions: `isSyncing`, `hasTrack`, `canEdit`.
- Routes: kebab-case, plural nouns — `GET /v1/hikes/:hikeId/waypoints`.

A markdown table defining the "Domain vocabulary" with columns for "Concept," "Use," and "Never," providing clear naming guidance.

Rule 7: Project structure

Without a map, agents infer structure by scanning the file tree, which wastes tokens and can still produce incorrect results. A simple structure guide is cheap to write and saves work every session.

claude.md
## Project structure

api/
  src/
    modules/<domain>/ # route.ts, service.ts, repo.ts, *.test.ts
    db/               # kysely client, generated types
    middleware/
    lib/              # shared, dependency-free helpers
    migrations/       # timestamped .sql, append-only
packages/
  contracts/          # zod schemas, OpenAPI output

- New backend work goes in a module folder. Routes never talk to the DB directly — route → service → repo.
- Nothing new at the repo root without asking.

A visual representation of a project's directory structure, complete with comments explaining the purpose of each folder.

Rule 8: End-to-end testing

Unit and integration tests catch isolated failures. End-to-end testing is what catches the interactions between pieces. Instruct the agent to drive the application like a human after any feature that spans both sides of the stack.

claude.md
## End-to-end testing

After any feature that spans both sides, exercise it like a person would.

- Boot the API and the simulator, sign in, and drive the actual flow end to end:
  start a hike, record a track, background the app, foreground it, sync,
  confirm the row in Postgres.
- Test the unhappy paths: airplane mode mid-sync, expired token, force-quit
  during a recording, duplicate submit.
- Report what you clicked and what you saw. If a step failed, keep the failure —
  don't work around it and call it passing.

Rule 9: UI testing

A green test suite says nothing about whether the screen looks right. A button could be covered by another element or text could be truncated by a long name. Use the agent's multimodal capabilities to inspect screenshots.

claude.md
## UI testing

A green test suite tells you nothing about whether the screen looks right.

- Screenshot every screen you touch, at least once, and look at it before you say it's done.
- Check the small iPhone and the largest Dynamic Type setting. Check dark mode.
- Use realistic seed data from `api/seeds/realistic.sql` — real place names, long names,
  empty states, a 400-item list. Never "Test User".
- Look specifically for: clipped or truncated text, overlapping views, content under the
  safe area, missing empty state, missing loading state.

Rule 10: Performance

Agents commonly fetch large datasets and filter them in application code rather than in SQL. Catch this before it reaches production by defining explicit performance budgets and database rules.

claude.md
## Performance

Every endpoint has a p95 budget of 200ms. Anything slower is a bug, not a tuning opportunity for later.

- Filter, sort, aggregate, and paginate in SQL. Never pull rows into Node to filter them there.
- Every list endpoint is paginated. `limit` defaults to 25, hard maximum 100. No unbounded lists.
- Select the columns you need. `select *` is not acceptable in a repo function.
- No N+1. Join or batch — one query per request path, not one per row.
- New query patterns come with an index in the same migration. Run `EXPLAIN ANALYZE`
  and include the plan in your summary if a query touches more than 10k rows.

A list of performance rules in a code editor, focusing on SQL best practices like pagination, column selection, and avoiding N+1 queries.

Rule 11: Error handling

Silent failures are the hardest bugs to track down. Define a strict policy that prevents errors from being swallowed and establishes a consistent response envelope.

claude.md
## Error handling

Fail early, fail loudly, and never swallow.

- No empty `catch`. No `catch { console.log(e) }`. Either handle it meaningfully or let it propagate.
- Throw a typed `AppError` with a stable machine-readable code. Unexpected errors surface as 500 —
  they do not get mapped to a friendly 200.
- Every error response uses this envelope:
  `{ error: { code: "hike_not_found", message: "That hike no longer exists.", details: unknown } }`
- Messages are useful to the client: what failed and what to do about it. "Something went wrong" is not an error message.
- Validation failures return 422 with the zod issue list in `details`.
- Log with structured context (`hikeId`, `userId`, `requestId`), never a bare string.

Rule 12: Architecture orientation

An agent that needs to re-discover your project's architecture at the start of every session spends tokens on exploration that a short reference section would eliminate. Give it a map.

claude.md
## Architecture

Read this before exploring the codebase.

Request path: SwiftUI view → Store → `APIClient` → Express route → service → repo → Postgres.
Auth is a short-lived JWT in the Keychain with a refresh token; `middleware/auth.ts` populates
`req.user` and every route below `/v1` requires it.

Sync is offline-first. The app writes to SwiftData immediately, queues a mutation, and reconciles
on reconnect. Server timestamps win on conflict; the client never invents an `id` — it sends a
client-generated `idempotencyKey`.

**Where things live:**

| You need | Look in |
| :--- | :--- |
| Auth, tokens, refresh | `api/src/modules/auth/` |
| Request/response schemas | `packages/contracts/src/` |
| DB client and generated types | `api/src/db/` |
| Migrations | `api/migrations/` |

The "Architecture" section of the `claude.md` file, which includes a high-level description of the request path and a lookup table for finding key files.

Putting it together

These rules work best as a starting skeleton rather than a final document. Start with the failure log section and the testing loop; add naming conventions and architecture orientation as soon as the codebase has enough shape to define them. Let the failure log grow from real mistakes rather than trying to anticipate every edge case upfront.

The goal is a file that's short enough to be reliable as context, specific enough to eliminate guessing, and alive enough to improve with every session that finds a new failure worth codifying.

Got an article suggestion? Let us know
Licensed under CC-BY-NC-SA

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.