Appearance
Every name a cold agent reads must convey its purpose without opening the file — because queryRequest tells an agent nothing, and FeaturePlanRequest tells it everything.
Principle
A Priori Friendly Naming
The test
Can the agent decide from the grep result alone?
agent $ grep -rn "def " src/
$rg "def " src/notion/
tags.py:12def process(data):must open file
tags.py:47def handle(item):must open file
tags.py:12def normalize_notion_task_tags(raw: dict):grep is enough
tags.py:47def dedupe_task_tags_case_insensitive(tags: list):grep is enough
A name is a prediction about what the body does — when the prediction is missing, the agent pays a roundtrip to read the body.
The violations
Generic becomes specific, or the name isn't doing its job.
Each row is a real violation. Left — zero signal. Right — full signal.
queryRequest→FeaturePlanRequest
process(data)→normalize_notion_task_tags(raw_tags)
Record<string, any>→interface FeaturePlanResponse
class Manager→class NotionTaskManager
raw dict payload→class NotionTaskTags(BaseModel)
If the name could plausibly belong to a different file, you haven't named it — you've placeholder'd it.
The scope
This is a code-construct rule — not a filename rule.
Governed
Apply a priori naming
- Function & method names
- Class names
- Type aliases & interfaces
- Pydantic model names
- Module-scope variables
The things agents grep for and read in signatures.
Out of scope
Different rulebook
- Filenames → Architecture is Agent Performance
- HTML elements & CSS classes
- Config keys → governed by schema
Named constructs live in signatures; filenames live in layouts.
Don't over-apply — and don't under-apply. The rule binds exactly where a cold agent reads the name before the body.
What named types buy you
A traceable flow across files, without a mental model.
One named type. Three sites. Grep stitches them into a story.
Created
FeaturePlanRequest
planner.py — builds the request
→
Transformed
FeaturePlanRequest
validator.py — adds constraints
→
Consumed
FeaturePlanRequest
executor.py — runs the plan
Record<string, any> — opaque, no breadcrumb, the agent must read every usage to trace the flow.
Named types are how agents navigate across file boundaries without loading the whole codebase into context.
The compounding cost
At one file it's noise. At a codebase it's a tax.
A codebase with 1,000 constructs. An agent invocation that touches 50 of them.
Violated — generic names
construct touches50
must open to infer50
tokens per open~800
orientation tax~40k tokens
Respected — dense names
construct touches50
must open to infer~3
tokens per open~800
orientation tax~2k tokens
Every agent invocation pays this tax on every construct it encounters — compounded across every call in the system's life.
The discipline
A name is a promise to a stranger — not a reminder to yourself.
Before you commit a name, ask: could a cold agent, reading this in a grep result with no other context, predict what it does? If not, rename it. The tax compounds on every invocation forever.