Skip to content

Navigability for AI Agents

An agent with a full context window still fails if the repo is a maze. Navigability is how quickly a cold session can locate the single authoritative place for a concern — and how hard it is to accidentally create a second one. Humans navigate by tribal knowledge; agents navigate by names, imports, and docs. Optimize for the latter.

What "navigable" means operationally

A repo is navigable when a fresh Claude Code session can, in under a few tool calls:

  1. Answer "where does billing proration live?" with one path (not three candidates).
  2. Answer "where do I add a new HTTP endpoint?" with a convention, not a guess.
  3. Avoid inventing a parallel helper because the real one was named utils2 or buried in an unrelated module.
SignalNavigableHostile
Feature codesrc/invoices/ owns invoice HTTP + domainLogic split across helpers/, services/, utils/invoice_stuff.ts
Cross-cuttingsrc/lib/money.ts is the only money helperformatCents reimplemented in 4 feature folders
Entry pointssrc/api/routes.ts or framework convention documented in CLAUDE.mdRoutes registered in 6 files with no index
DocsARCHITECTURE.md maps domains → directoriesREADME is setup-only; architecture is oral

See Repository Structure for layout patterns; this page is about the agent-facing properties of whatever layout you chose.

Naming that agents can grep

RuleDoDon't
Name by domain + roleInvoiceProration, invoice_proration.tshelper, manager, processor, stuff
One concept, one stemProration everywhereProration / ProRate / BillingAdjust
Match route to moduleGET /invoicesinvoices/GET /invoices implemented under billing/legacy/
Avoid false friendsDon't name a DTO InvoiceServiceAgents will put business logic in anything called *Service

Put the naming rules in CLAUDE.md as greppable never-dos:

text
- Money math lives only in src/lib/money.ts — never reimplement cents conversion.
- New HTTP handlers go under src/api/<resource>/ — never add routes in src/lib/.
- Do not create files named utils.ts, helpers.ts, or common.ts — name the domain.

Module boundaries agents respect

Agents follow import graphs more faithfully than prose architecture diagrams. Make the illegal import structurally awkward:

Boundary ruleEnforcement that works with agents
Domain must not import APILint/import rule + CLAUDE.md never-do
No feature imports another feature's internalsPublic barrel only; deep imports flagged in review
Shared code earns its place in lib/"Promoted only when used by 2+ domains" — written down

Without enforcement, agents will "just import" across layers to finish the task. With a lint rule, the agent hits a red gate and usually corrects course. See Architecture Erosion.

Index files and "where does X live" conventions

Barrel / index files help when they are curated public APIs, not dumping grounds:

PatternUse whenAgent failure if misused
src/domain/invoices/index.ts exports public types/functionsClear module APIBarrel re-exports everything → agents import internals "through" the barrel anyway
docs/map.md or ARCHITECTURE.md table: Domain → PathMulti-package monoreposStale map → agent trusts the wrong path
CLAUDE.md "Where things live" section (≤15 lines)Every non-trivial repoMissing section → agent searches and invents

Example ARCHITECTURE.md fragment agents actually use:

markdown
## Where things live
| Concern | Path | Notes |
|---|---|---|
| HTTP routes | `src/api/<resource>/` | Register in `src/api/routes.ts` |
| Invoice domain | `src/domain/invoices/` | No Stripe imports here |
| Stripe adapter | `src/adapters/stripe/` | Only place that imports `stripe` |
| Money | `src/lib/money.ts` | Integer cents only |

Preventing duplicated logic

Duplication is the #1 navigability killer under agents: they find a place that almost fits and copy it.

PreventionHow
Single authoritative moduleDocument it; link from CLAUDE.md
Search-before-create rulePrompt + CLAUDE.md: "rg for existing helpers before adding new ones"
Characterization of shared behaviorTests in the shared module so copies drift loudly
Review grepOn PRs, search for near-duplicate function names

Prompt fragment:

text
Before adding any shared helper, search the repo for existing implementations
(rg -i proration, cents, money). If one exists, extend it. If two exist,
STOP and report duplication — do not create a third.

Anti-patterns agents exploit

Anti-patternWhat the agent doesCountermeasure
utils.ts / helpers.ts grab bagsDumps new functions there or creates utils2.tsBan the names; require domain-named modules
Parallel "v2" packagesCreates invoices-v2/ beside invoices/Never-do + review reject; migrate or extend in place
Copy-paste adjacent featureClones credits/ to make debit/ with driftSpec "extend shared X"; provide the path to extend
God services/ layerPuts everything in InvoiceService classPrefer functions in domain modules; limit class size in CLAUDE.md
Undocumented side entrancesAdds a one-off script that bypasses domain rulesScripts must call domain API; note in navigability map
Comments as architectureRelies on stale "see Foo" commentsExecutable structure + short map; delete lying comments
Generated + hand-edited mixEdits generated clients in placeCLAUDE.md: never edit **/generated/**
markdown
- [ ] Fresh session: "Where does <core domain rule> live?" — one answer?
- [ ] `ARCHITECTURE.md` / map matches reality (spot-check 5 rows)
- [ ] No `utils.ts` / `helpers.ts` at repo root of src
- [ ] Import lint (or equivalent) still fails on a deliberate layer violation
- [ ] Duplicate-stem search: `rg "function formatCents"` → ≤1 definition
- [ ] CLAUDE.md "Where things live" ≤15 lines and accurate
- [ ] Last 10 agent PRs: count of drive-by new modules — trending down?

Making navigability stick

  1. Write the map when the structure is still small (New Project).
  2. Encode never-dos in CLAUDE.md — agents load them every session.
  3. Enforce with gates where possible (import rules beat prose).
  4. Reject navigability regressions in review as hard as logic bugs — a second money.ts is a permanent tax on every future agent run.
  5. After recovery from a messy agent PR, update the map if the agent got lost for a structural reason — see Recovery.

A field manual for AI-native software engineering.