Codebase rules for agents
Monday, 9:20, week 4. Eight engineers run about 25 Claude Code sessions a day. Over two weeks, reviewers wrote the same six comments seventeen times. And last Thursday, one edited database file blocked every team's deploy for five hours.
Every agent session starts with no memory of yesterday. Write short rules where it will read them, show it the right example, and make the dangerous things impossible.
- Problem
Week 4 of StayHub for Chains: 8 engineers run about 25 Claude Code sessions a day. In two weeks, 17 review comments repeated the same 6 problems: home-made date helpers, direct SQL on the rates table, the wrong test command, money as floats, guest names in logs, and an edited migration.
- Why the simple system fails
Each session starts fresh and learns the codebase from what it reads. The root CLAUDE.md is 640 lines, 14 months old and contradicts itself, so agents follow whichever line they happen to weigh, and reviewers repeat the same comments forever.
- Engineering concept
Rules are part of the codebase: a short root CLAUDE.md (what this is, how to verify, conventions, boundaries, never-touch), per-directory files where rules differ, golden paths to copy, and tools (lint, CI checks, permissions, hooks, CODEOWNERS) for anything that must never happen. A review comment made twice becomes a rule or a check.
- Technology
CLAUDE.md files at the root and in subdirectories, Claude Code permission settings and hooks, lint rules for import boundaries, CI checks, CODEOWNERS, reference implementations.
- Trade-offs
Text rules are cheap and flexible but only guidance; enforced checks are reliable but cost effort and can block legitimate work. Long rule files feel thorough but get diluted and stale; short ones need pruning discipline.
The problem
2 min- You · Engineer at StayHub61 PRs in two weeks, 17 review comments repeating 6 problems. Five PRs added their own date helper.
- Omar · On-call SREAnd Thursday's edited migration froze every team's deploys for 5 hours.
- Lina · Senior engineer, your mentorWhat did those sessions read before they started?
The big idea
4 minShort text for guidance, tools for guarantees. Every rule comes from a real, repeated problem, and lives next to the code it governs.
A kitchen taking new temporary cooks every day has a one-page card on the wall, a card per station, a reference plate for each dish, and red boards for raw meat so nobody has to remember. Agents arrive new every morning.
How it works
5 min- 1Root CLAUDE.md: one page
is read at the start of every session. About 40 lines: verify commands (
pnpm test:chains, ~2 min, not the 25-minute suite), conventions, boundaries, a short never list. - 2Per-directory files, owned
Files in subdirectories add rules for that area:
src/rates/CLAUDE.mdwritten and owned by the Rates team,db/migrations/CLAUDE.md: always add, never edit. - 3A golden path
A is one real, blessed example per common task. "New endpoint? Copy
src/chains/ari/availability/." Agents copy what they see. - 4Tools for incident-grade rules
A runs at set moments and can block an action; names required approvers. Plus lint boundaries, CI checks and permission denies. One miss of these is an incident.
- 5Text explains the tools
Keep a line for each enforced rule so the agent knows what to do instead: "never edit an applied migration: add a new one".
- 6Upkeep
A comment made twice becomes a rule or a check that week. Prune monthly; delete old lines, don't archive them in the file.
Watch a decision being made
4 minYou draft the rules; Lina comments twice
Turn two weeks of repeated comments into rules every session follows, and block the dangerous mistakes.
- 1Collect what really repeats
Claude groups every review comment on the 61 PRs: dates ×5, test command ×4, rates table ×3, floats ×2, guest names in logs ×2, applied migration ×1.
Why: Rules come from evidence of real mistakes, not a wish list of good practices.
- 2One page, old file deleted
Verify commands, four conventions (
src/lib/dates, integer cents + currency,redact()in logs), boundaries throughsrc/<module>/public, never list. Lina: "Delete the 640 lines; don't archive them in the same file."Why: Line 40 said "use RatesClient v1", line 512 said it's deprecated. Contradictions make agents unpredictable.
- 3Ask which rules would be an incident
Lina: "Your never list is a wish list." Enforced: import-boundary lint, migration hook + CI checksum check, permission deny on
pnpm db:migrate:*, CODEOWNERS onsrc/rates, a log scan for guest names.Why: Text can be missed; tools can't be talked around.
- 4Set the upkeep habit
Twice → a rule or a check, same week. First Monday of the month: prune. Each per-directory file names its owner.
Why: Without a habit, you're back to 640 lines in a year.
In real life
3 minGoogle: rules and checks
- Shared style guides
- Code owners must approve
- Heavy automated checking
Consistency by enforcement.
Netflix: the paved road
- Supported default tools
- Teams may leave the road
- And own the extra cost
Consistency by making the right way easiest.
What goes wrong
3 minOne miss is an incident.
Hooks, permission denies, CI checks, CODEOWNERS.
The same comment returns next week.
Twice → a rule or a check.
Nothing to check; contradictions make agents unpredictable.
Concrete rules ("integer cents + currency"), an owner per file, monthly pruning.
Trade-offs
3 min| Rules style | Fits when | You pay |
|---|---|---|
| Everything in one big file | Never past a small project | Diluted, stale, contradictory |
| Short layered text + enforced musts | Several teams or many agent sessions | Owners and pruning |
| Only tools | Purely mechanical rules | No context; confusing failures |
- Karim · CTOWhy a page? Put the whole engineering handbook in CLAUDE.md. Agents read fast.
- Omar · On-call SREText is a suggestion; Thursday proved it. Enforce everything in CI and hooks.
- Karim · CTOYou can't lint "copy the golden path".
Your job as tech lead
4 minClaude Code can write the code for this. You still own the decisions, and you have to know what good looks like to accept or reject its work.
- Which rules exist at all: only ones that answer a real, repeated problem.
- Which are guidance (CLAUDE.md) and which are guarantees enforced by a tool, and where each lives with its owner.
- Changes to CI config, CODEOWNERS, permissions and the rules themselves: never made by an agent silently.
Read every review comment on chains PRs from the last two weeks (use gh) and the current CLAUDE.md. Group comments into repeated problems with counts and links. Draft a root CLAUDE.md of at most 45 lines: project, verify commands, conventions, boundaries, never, when unsure. Every rule must cite a repeated problem or ADR. For each never rule, propose a tool to enforce it, but don't change settings, CI or CODEOWNERS.
- Every rule traces to a review comment, incident or ADR.
- You ran the verify commands yourself and know how long they take.
- Each never rule's tool was tested once: try the forbidden edit, see it blocked.
- A rules file that grows with every incident and never shrinks.
- The agent editing CLAUDE.md, CI or permissions to make its own change pass.
- A never rule still only text after it caused an incident.
Check yourself
2 minSelect all that apply.
Next: with good rules, every engineer starts running three sessions at once. Twenty-three pull requests are now waiting for review, and main broke twice this week.
Mark it complete to update your skill map.
Go deeper (optional)You don't need this to understand the lesson. Open it when a project needs the details: Technical deep dive, Analogy, Why it matters, Check your understanding, Practice ladder, Explain it yourself.
Technical deep dive
Rules that end the seventeen comments
Turn two weeks of repeated review comments into a rules system that every agent session follows and that blocks the dangerous mistakes.
- 1Collect what really repeats
Ask Claude Code to list every review comment on the 61 chains PRs and group them. Six problems cover 17 comments: dates ×5, rates table ×3, test command ×4, floats ×2, guest names in logs ×2, applied migration ×1.
Why: Rules should come from evidence of real mistakes, not from a wish list of good practices.
- 2Write a one-page root file
Verify commands, the four conventions, boundaries, never list, "when unsure, stop and ask". Delete the old 640 lines; link ADRs instead of copying them.
Why: Short rules get followed; contradictions and stale lines are what made agents unpredictable.
- 3Push area rules to their owners
src/rates/CLAUDE.mdby the Rates team (private table,upsertBatch(), 500-row batches);db/migrations/CLAUDE.md(always add, never edit);src/chains/CLAUDE.md(queue keys, golden path).Why: Rules next to their code are read when relevant and updated by the people who change that code.
- 4Name the golden path
src/chains/ari/availability/: handler, input schema, queue publish, tests. You review it once more so it's worth copying.Why: Agents copy what they see. A good example carries conventions that would take pages to describe.
- 5Enforce the incident-grade rules
Import-boundary lint rule (fails
pnpm checkand CI); hook blocking edits to applied migrations + CI checksum check; permission deny onpnpm db:migrate:*; CODEOWNERS onsrc/rates; a test that scans log output for guest names.Why: Each of these would be an incident, and one occurrence is enough. Text can be missed; tools can't be talked around.
- 6Set the upkeep habit
A comment made twice becomes a rule or a check, in the same week. First Monday of the month: prune rules a tool now enforces or that no longer apply. Each per-directory file names its owner.
Why: Rules rot as the code changes. Without a habit, you're back to 640 lines in a year.
| Repeated problem | Rule (where) | Enforced by |
|---|---|---|
| Home-made date helpers (×5) | Root: only src/lib/dates | Lint rule in src/chains |
| Rates table accessed directly (×3) | src/rates/CLAUDE.md | Import-boundary lint + CODEOWNERS |
| Wrong or no test command (×4) | Root: verify section | CI runs the full suite on every PR |
| Money as floats (×2) | Root: integer cents + currency | Money type; floats don't compile |
| Guest names in logs (×2) | Root: redact() | Test scanning log output |
| Applied migration edited (×1) | Root + db/migrations/CLAUDE.md | Hook + CI checksum check |
Analogy
- Kitchen with temp cooksOne card on the wallCodebase with agentsShort root CLAUDE.md
- Kitchen with temp cooksA card per stationCodebase with agentsA file per folder
- Kitchen with temp cooksReference plate to copyCodebase with agentsGolden path example
- Kitchen with temp cooksRed boards, fryer cut-offCodebase with agentsLint, CI, hooks, permissions
Where it breaks: a temp cook remembers the kitchen tomorrow. A new agent session doesn't, which makes the written rules even more important.
Why it matters
Reviewers repeat themselves. Seventeen comments in two weeks, the same six problems. That's review time, the project's scarcest thing, spent on copy-paste.
Parallel sessions multiply mistakes. Twenty-five sessions a day, each inventing its own date helper, means twenty-five different time-zone bugs.
Long rules get ignored. A 640-line file that contradicts itself buries the one rule that mattered. One edited database migration blocked every team's deploy for five hours.
Check your understanding
Practice ladder
What happens to each session?
Scenario Warm-upThe new rules and tools are in place (root CLAUDE.md, per-directory files, import-boundary lint, migration hook + CI checksum check, permission deny on pnpm db:migrate:*, CODEOWNERS on src/rates, a log-scanning test for the ARI module). Trace what happens in each case:
- A session in src/chains imports
src/rates/internal/tableto save a query. - A session tries to run
pnpm db:migrate:staging"to check the migration works". - A session tries to edit
db/migrations/0412_hotel_mappings.sql. - A session in the new src/chains/reports module logs the full booking payload, including guest names.
- A session changes
src/lib/dates, runspnpm test:chains(green) and says "tests pass".
Complete the root CLAUDE.md
Design PracticeFill the blanks.
## Verify your work (always run before saying done) - Typecheck + lint: ___ - Tests for one module: ___ (e.g. ___, about ___ min) - The full suite takes ___ min: ___ runs it. ## Conventions - Dates and time zones: only via ___ - Money: ___ + ___. Never ___. - Logging: src/lib/log with ___; never log ___. - New endpoint? Copy the golden path: ___ ## Boundaries - A module imports another only through ___. ___ enforces this. ## Never - Edit a migration that is already applied: ___ - Run migrations or scripts against ___
Rules for the statements module
Design StretchThe consolidated monthly statement (M2) gets its own module: src/chains/statements. It computes commission per hotel, groups by legal entity, and produces files Atlantica imports into accounting. Finance Engineering owns the invoicing code it reads from.
Without help, write src/chains/statements/CLAUDE.md (8–12 lines), and list which of its rules you'd enforce with a tool, and which tool.
The rules grew back
Decision StretchWeek 12. The root CLAUDE.md has grown from 40 to 310 lines: every incident and review added a paragraph. Review comments are rising again. Two proposals:
- An engineer: add a "rules reviewer" sub-agent that checks every PR against all 310 lines and comments.
- Omar: freeze the file and add no more rules.
A third option is yours to define. Write your decision: "We chose X over Y because…, accepting the risk of…".
Explain it yourself
A colleague says: "Agents are smart; just tell them in the chat what you want each time." Explain why the chains project needs written, layered and enforced rules instead.