← Back to writing

Writing Code for the Agentic Reader

·5 min read Software Engineering

Most codebases were written for two readers: the compiler and the human teammate. The compiler needs precision. The teammate needs intent. A third reader has quietly entered the loop: the coding agent, arriving fresh every session with no lived memory of the repository, a limited context window, and a habit of navigating by text search.

That changes the practical meaning of "clean code." It is no longer enough for a function to be elegant once opened. It must also be findable before it is opened. A good name is not just decoration; it is an address. A file path is not just organization; it is a query surface. A type is not just safety; it is executable documentation that can force the agent back onto the right path.

The Core Philosophy

At its heart, agent-readable code prioritizes discoverability and semantic friction over clever compression. It assumes the next worker in the codebase may not browse like a senior engineer with years of context. It may instead run rg, inspect a few windows around promising matches, and loop until it has enough confidence to edit.

That is not a weakness. It is a constraint, and constraints are where durable design begins.

If a symbol cannot be found by the words someone would naturally search for, it is only half documented.

The strongest idea from Ben Vinegar's Modem article is simple: agents mostly read repositories through strings. They search function names, filenames, error literals, type names, and nearby comments. Generic words like create, data, handler, or config explode into hundreds of candidates. Domain-rich names like createStripeClient, validateNotificationDeliveryConfig, or hmacPayloadSigner collapse the search space into something useful.

This means a codebase can be technically correct and still expensive to understand. Every false hit burns context. Every ambiguous file forces the agent to spend tokens ruling out near-misses. Worse, the model may believe those near-misses. A vague name gives wrong code permission to look relevant.

The answer is not maximal verbosity everywhere. The answer is names with enough domain signal to resolve in one or two searches. Two to four words is often the useful range: action, domain, object, and sometimes constraint. diffUserObjects beats diff. formatInvoiceDuration beats formatDuration when multiple durations exist. sessionBrokerLeaseStore beats store because the path is not always visible where the search lands.

Implementation in TypeScript

Translating this philosophy into code requires treating every exported symbol as a search contract. The agent will not always read the folder tree first. It will not always follow the import graph correctly. It will often meet your code as a single line in a grep result.

Consider the difference between a generic helper and a discoverable one:

// Hard for an agent to locate with confidence.
export function create(config: Config) {
  return new Client(config)
}

// Searchable in both directions: definition and call sites.
export function createStripeBillingClient(config: StripeBillingConfig) {
  return new StripeBillingClient(config)
}

The second version is longer, but it gives the agent three handles: Stripe, Billing, and Client. It can find the definition, distinguish it from other clients, and find call sites without opening half the repository.

Types should carry the same burden. A signature like this looks typed, but still permits silent confusion:

function transferProjectOwnership(userId: string, orgId: string, projectId: string) {
  // ownership transfer
}

An agent can swap those strings and the compiler will shrug. Branded IDs turn the mistake into a navigable error:

type UserId = string & { readonly brand: 'UserId' }
type OrgId = string & { readonly brand: 'OrgId' }
type ProjectId = string & { readonly brand: 'ProjectId' }

function transferProjectOwnership(userId: UserId, orgId: OrgId, projectId: ProjectId) {
  // ownership transfer
}

Now the compiler becomes a collaborator. If the agent gets the order wrong, the error names the exact concept it needs to search. That is the hidden power of precise types: they make incorrect edits noisy, and the noise contains useful words.

Comments also need to move closer to where search lands. A paragraph in a README is valuable, but the most reliable documentation is the one-line note directly above the exported function, type, or constant. The agent searched the name and arrived at the definition; meet it there with the sharpest constraint the signature cannot express.

/** Computes retry delay in milliseconds; uses attempt count, not wall-clock time. */
export function computeNotificationRetryDelayMs(attempt: number): number {
  return Math.min(30_000, 2 ** attempt * 250)
}

The comment says "retry delay" in plain words. The function says Notification, Retry, Delay, and Ms. The unit is encoded twice: once in the name for search, once in the doc comment for judgment. This is not ceremony. It is a small toll paid once so every future agent and human pays less.

The same rule applies to files. A filename like helpers.ts is a dead end. A filename like notification-retry-backoff.ts is a map. Tests should mirror the source they cover. Deprecated paths should say @deprecated where the agent will see them. Error messages should begin with stable, searchable literals instead of being assembled from fragments no one can grep.

The Future of Code

As agentic software work becomes normal, the difference between readable code and retrievable code will collapse. Code that agents can navigate cheaply will be changed more safely. Code that agents cannot find will become a tax on every task: more tokens, more turns, more false confidence, more human review required to catch edits made from partial context.

The irony is that this is not a new discipline. Descriptive names, precise types, small modules, colocated tests, and comments that explain the why have always helped humans. Agents simply remove our ability to cheat. They do not know that utils.ts is secretly where billing proration lives. They cannot remember last quarter's Slack thread. They have the repository text, the compiler, and a search box.

So the design target becomes brutally simple: write code that can be found, parsed, and trusted from the search result outward.

Credit: this essay is informed by Ben Vinegar's Modem article, How coding agents read your code (and how to write for them), and Modem's write-discoverable-code skill.

Tagged:AICodeAgents