How to Write Navigable Code for Coding Agents

When a coding agent opens a repository, it usually does not start by writing code. It reads instructions, searches for symbols, opens files, follows imports, inspects tests, and builds a partial picture of where the change belongs. Only then does it edit anything.

That makes the repository more than a set of instructions for a compiler. It is also an interface the agent has to navigate.

This reframing has a practical consequence. The same properties that help humans find their way around a codebase—clear names, meaningful boundaries, and visible dependencies—also influence how much work an agent must do before and after an edit. If a concept has a name the agent can search for, it has a route into the code. If a dependency is implicit, scattered, or lexically unrelated to the task, the agent may never discover it.

I want to be honest about the payoff because the evidence is narrower than the usual “clean code makes AI better” claim. In a controlled study, cleaner and messier versions of the same repositories produced essentially the same pass rate: 91.3% on the cleaner side and 92.1% on the messier side. Navigability did not make the agent more correct in that experiment. It changed the cost and shape of the journey.

This article turns that result, and related evidence about identifiers, structural navigation, and long-running agent work, into practical rules. It is a companion to How to Build a Reliable Coding-Agent Harness. That article is about the system around the model: maps, controls, oracles, evidence, and verification. This one is about the code inside that system.

Most of the research cited here is recent preprint work. Some studies use one model, one harness, or one repository, and the practices below have not been evaluated as a complete package. I will distinguish the measurements from my engineering interpretation instead of pretending the research proves more than it does.

Code is a navigation interface

A task normally arrives in the language of behavior:

Normalize the search query before checking the result cache.

The repository does not hand the agent a perfect map from that sentence to the implementation. The agent has to construct one from what it can observe: filenames, identifiers, imports, call sites, tests, configuration, comments, documentation, and search results.

A simplified trajectory looks like this:

  1. Discover: find candidate files and symbols from the task vocabulary.
  2. Map: follow relationships until the likely change surface is complete.
  3. Edit: implement the behavior in that surface.
  4. Recheck: revisit files, tests, and callers to reduce the chance of missing something.

The final edit may be small while the route to it is long. A five-line patch can require reading twenty files because the agent does not know in advance which five matter.

Writing navigable code is therefore not about optimizing the destination. The destination is still correct behavior. It is about shortening and clarifying the route: making the first useful search more likely to land near the right code, making related responsibilities easier to follow, and making non-obvious relationships visible before they become omissions.

This is also why “put the whole repository in the context window” is not a complete answer. Capacity and discovery are different problems. A file that could fit in context still provides no value if the agent never has a reason to open it.

What navigability buys you—and what it does not

The 2026 preprint Does Code Cleanliness Affect Coding Agents? created six minimal repository pairs across Java and Python. Each pair matched architecture, dependencies, tests, and external behavior while differing in static-analysis violations and cognitive complexity. The authors ran 33 tasks ten times on each side, producing 660 Claude Code trials with Claude Sonnet 4.6.

Aggregate pass rate barely moved: 91.3% on cleaner code and 92.1% on messier code. The operational footprint did move. On the cleaner variants:

  • input tokens fell 7.1%;
  • output tokens fell 8.5%;
  • reasoning characters fell 11.1%;
  • conversation messages fell 7.0%;
  • file revisitations fell 33.8%.

The study did not directly measure confidence or confusion. File revisitation is an observable behavior, not access to the model’s internal state. Still, repeated reading is a reasonable proxy for uncertainty in the route: the agent is returning to material it already inspected instead of moving directly through the change.

The cost matters because agentic coding is already resource-intensive. How Do AI Agents Spend Your Money? evaluated eight frontier models on SWE-bench Verified with four runs per problem. In that setup, agentic tasks consumed roughly 1,000 times more tokens than the code-reasoning and code-chat comparisons. Runs on the same task differed by as much as 30 times in total tokens, and accuracy often peaked at an intermediate cost before saturating at higher cost.

These studies do not show that navigable code eliminates cost variance. They do show why avoidable navigation is worth removing. More tokens are not automatically more useful work.

The defensible claim is narrow:

Navigability is a cost-and-navigation lever, not a correctness guarantee.

You still need specifications, tests, review, and a harness that can contradict a bad implementation. Navigable code helps the agent reach and inspect the change surface with less waste. It does not prove that the resulting patch is right.

Names are the retrieval interface

When an agent translates a natural-language task into repository locations, names are the bridge.

If the task says “normalize the search query,” this symbol is a useful search target:

export function normalizeSearchQuery(query: string): string {
	return query.trim().toLocaleLowerCase();
}

This one may behave identically, but it removes the bridge:

export function xfmQ2(value: string): string {
	return value.trim().toLocaleLowerCase();
}

The When Names Disappear study helps explain why. Its authors applied semantics-preserving identifier obfuscation and measured code summarization and execution prediction. On ClassEval class-level summarization, GPT-4o’s score fell from 87.3 on original code to 58.7 on obfuscated code. Other models also declined, although by different amounts. The identifiers were carrying intent that structure alone did not reliably recover.

That experiment evaluated summarization, not an agent searching a repository. It does not directly prove that normalizeSearchQuery lowers navigation cost. My engineering inference is that names which preserve intent give lexical tools—grep, symbol search, BM25, and the model’s own reasoning—better anchors.

The useful rule is not “make every name long.” It is:

Use stable domain and behavior vocabulary where someone is likely to search for it.

Prefer the words used by requirements, public APIs, user-visible behavior, and established domain language. Avoid private shorthand unless it is already universal in the codebase. Apply the vocabulary consistently across the implementation, tests, configuration, and documentation so one search reveals a coherent path.

Names also need to remain honest. A beautifully greppable name attached to a mixed or misleading responsibility is worse than a plain one. Retrieval gets the agent to a symbol; the symbol’s boundary must still match the concept it advertises.

Structure that helps versus structure that scatters

“Make functions smaller” is not a sufficient rule for agent-friendly code. Smaller units can create precise search targets, or they can spread one task across more places without creating any new meaning.

The cleanliness study found both outcomes. Across 14 multi-module tasks, the cleaner variants used 10.7% fewer input tokens and had 50.8% fewer file revisitations. Across 13 cognitive-hotspot tasks, however, distinct files read increased 11.2% and input tokens increased 1.8%.

Two case studies make the contrast concrete.

In one Java bytecode-disassembly task, the cleaner variant replaced opcode dispatch inside two large switch-based methods with thin dispatchers and roughly ten named helpers each. Agents used 35% fewer input tokens, opened 25% fewer files, and took 32% fewer conversation turns. The files were not meaningfully smaller. The improvement came from helpers that acted as precise grep targets.

In a cluster job-limit task, helper extraction left the focal launch logic in place while spreading the surrounding work across more methods. On that cleaner variant, input tokens increased 8%, while most other metrics stayed within a few percentage points of zero. The refactoring added surface without giving the agent a better route.

The practical rule is:

Extract a boundary when it creates a discoverable concept, not merely because a function is long.

Before splitting code, I find these questions more useful than a line-count threshold:

  • Does the new unit own a recognizable domain behavior, policy, protocol, or transformation?
  • Would a task naturally use words that match its filename or symbol name?
  • Can a reader understand its contract without reopening the implementation it came from?
  • Does it centralize related behavior, or force the same change to touch more locations?
  • Will a caller need fewer facts after the extraction, or just more jumps?

A good boundary compresses knowledge behind a name. A bad extraction redistributes the same complexity and increases the number of places an agent has to inspect.

The dependencies code search cannot reveal

Names solve semantic discovery: the task and the code share vocabulary. Some dependencies are structural instead. They matter because of imports, inheritance, instantiation, registration, events, or dependency injection, even when they share almost no words with the request.

The CodeCompass study calls this the Navigation Paradox. Its clearest example asks an agent to “add a logger parameter to BaseRepository.__init__.” A complete change also requires editing app/api/dependencies/database.py, which instantiates repositories through get_repository(). That file has no lexical overlap with “logger,” “parameter,” or “BaseRepository.”

On the study’s hidden-dependency tasks, vanilla navigation achieved 76.2% final required-file coverage and BM25 retrieval achieved 78.2%. Graph navigation over imports, inheritance, and instantiation reached 99.4%. This was a coverage result on one FastAPI repository, not proof that 99.4% of implementations were correct. It nevertheless exposes a real limit of naming: semantic similarity cannot reveal every architectural relationship.

There are two responses, and mature codebases usually need both.

The harness-level response is structural tooling: dependency graphs, call hierarchies, symbol references, and mandatory impact analysis. I cover that side in the reliable harness article.

The code-level response is to make composition and registration points explicit and searchable. Prefer named factories and composition roots over important wiring hidden behind reflection or auto-discovery. When a relationship remains non-obvious, leave a short, durable signpost at the wiring point:

import { OrderRepository } from "./order-repository";
import type { Database } from "../database";
import type { Logger } from "../logging";

export interface OrderRepositoryDependencies {
	database: Database;
	logger: Logger;
}

// Production composition root for OrderRepository.
// Test wiring is centralized in createOrderRepositoryFixture.
export function createOrderRepository(
	dependencies: OrderRepositoryDependencies,
): OrderRepository {
	return new OrderRepository(dependencies.database, dependencies.logger);
}

The factory name makes an instantiation search productive. The dependency object makes constructor requirements visible. The comment points to the separate test composition root using a symbol that can also be searched.

Comments are a fallback, not a substitute for structure. “Remember to update another file” will rot. A useful signpost names the relationship and the exact symbol, registry, or composition root that owns it.

Agents erode structure, so guidance must be continuous

There is a tension at the center of this practice: we want agents to navigate clear code, but repeated agent changes can make that code harder to extend.

Version 2 of SlopCodeBench evaluated 15 coding agents on 36 problems containing 196 sequential checkpoints. At each checkpoint, an agent extended its own previous implementation under an evolving external specification. The benchmark tracked two deliberately limited quality dimensions: verbosity, meaning redundant code, and structural erosion, meaning complexity concentrated in already-complex functions.

No evaluated agent completed an entire problem end to end, and the best strict pass rate was 14.8% of checkpoints. Structural erosion increased in 77% of trajectories and verbosity in 75.5%. Compared with a panel of 473 open-source Python repositories, agent checkpoints averaged 2.0 times more structural erosion and 2.3 times more verbosity.

Explicit quality prompts improved the starting point. Depending on the model and prompt, initial erosion fell by as much as 62.3% and initial verbosity by as much as 34.8%. But average quality velocity remained about 1.3 percentage points of degradation per checkpoint. Across the prompt strategies, cost per checkpoint increased 12.1% on average, and aggregate correctness fell 2.3 percentage points.

The study does not define all of maintainability through two metrics, and it does not prove that every agent-assisted repository will degrade. It shows that a one-time “write clean code” request did not arrest the measured drift in this benchmark and introduced trade-offs of its own.

My engineering conclusion is that navigability cannot be a slogan at the top of a prompt. It has to be part of implementation instructions, review criteria, and longitudinal observation. The correction has to recur because the pressure that erodes the structure recurs.

How to direct agents to write navigable code

The following practices are a synthesis of the evidence, not a bundle that any of the studies evaluated directly. Treat them as hypotheses to apply proportionally and inspect in your own codebase.

1. Encode naming and boundary rules as instructions

“Write clean code” leaves the important decisions undefined. Tell the agent what navigability means in your repository.

This is a starting point I would adapt to the project’s domain language and architecture:

## Navigability rules

- Name files, types, and functions with the domain and behavior vocabulary used
  in requirements, public APIs, and tests. Avoid private shorthand for concepts
  someone will need to search for.
- Extract a helper or module only when it creates a stable, named boundary that
  a future task could discover. Do not split code only to reduce line count.
- Keep one behavior's change surface cohesive. Do not scatter a small change
  across new files without a contract or ownership boundary that justifies it.
- When changing a constructor, public signature, event, registry, or configuration
  key, trace its callers, instantiation sites, registrations, and tests.
- At non-obvious composition or registration points, add a short signpost naming
  the related symbol or owner. Do not add vague "update elsewhere" comments.
- Make the smallest coherent change that satisfies the task. Do not perform
  adjacent refactors or add speculative abstractions unless the task requires them.

These instructions still rely on judgment. Their value is that they turn an abstract preference into choices an implementation or review agent can examine.

2. Make wiring a first-class search surface

Factories, dependency-injection containers, route tables, event registries, serializers, and plugin lists are common hidden-dependency sites. Give them explicit names and stable locations.

For example, if all production construction of OrderRepository flows through createOrderRepository, a constructor task can reach the composition root by searching for either symbol. If construction happens through reflection based on a string in an unrelated file, the same task may have no lexical route to it.

Centralization alone is not the goal. A giant service locator can become its own hotspot. The goal is a small number of explicit composition surfaces whose names describe what they wire.

3. Add a navigability pass to review

Correctness review asks whether the patch implements the requested behavior. A navigability pass asks what route the next change will have to take.

In my experience, this works better as a focused review than as one more sentence inside a broad “review everything” prompt:

Review the changed code for navigability. Do not propose unrelated refactors.

Report exact files and symbols for each finding. Check whether:

1. the task vocabulary can find the changed behavior by filename or symbol search;
2. a new constructor, registration, event, or configuration dependency is hidden;
3. an extraction created a meaningful boundary or only added navigation hops;
4. one behavior is now scattered across locations without clear ownership;
5. comments at wiring points name durable symbols instead of vague instructions.

If no concrete issue exists, say so rather than inventing one.

A reviewer agent is not an independent oracle by default. It may share the implementation agent’s blind spots. The pass is useful because it changes the question being asked and demands concrete locations, not because a second model is automatically objective.

4. Enforce the smallest coherent change

“Smallest change” is easy to misread as “fewest changed lines.” A dense patch inside the wrong module can be smaller and still make future work harder.

The better target is the smallest coherent change: all behavior required by the task, placed behind the right existing boundary, without adjacent cleanup or abstractions for hypothetical future requirements.

This reduces two common navigation regressions:

  • opportunistic refactoring that changes names and locations unrelated to the task;
  • speculative layers that add files and indirection before there is a stable concept to name.

Sometimes the smallest coherent change includes a refactor because the current boundary cannot express the behavior safely. When that happens, require the agent to explain which responsibility moves, what new contract appears, and how the new name improves discovery. “The function was long” is not enough.

5. Observe navigation over a series of changes

SlopCodeBench is a reminder that a good-looking patch says little about the fifth or twentieth extension. If your harness records trajectories, watch navigability as a trend:

SignalWhat it may revealImportant caveat
Files revisitedThe agent repeatedly rechecks an unclear change surfaceSome revisits are legitimate verification
Distinct files readA concept may be scattered or poorly localizedCross-cutting tasks naturally touch more files
Time or tokens before the first relevant editSearch terms may not lead to the implementationModel and harness changes also affect the number
Repeated hotspot growthNew behavior keeps accumulating in an already-complex boundaryComplexity metrics do not capture every design quality
Unrelated files changedScope control or ownership boundaries may be weakA hidden dependency can make a distant edit necessary

Do not turn these signals into universal thresholds without calibration. Compare similar tasks, repeat runs when cost permits, and inspect the trajectory behind an outlier. A low token count is not proof of good architecture, just as a high count is not proof of bad architecture.

No study cited here has shown exactly how the per-task footprint effect from cleaner code compounds into long-horizon erosion in the same repositories. Monitoring that connection is a reasoned practice and an open empirical question, not a proven formula.

What the evidence does not prove

The central limitations are worth collecting in one place:

  • The cleanliness study found lower operational footprint, not higher correctness, and used one model and harness across six controlled repository pairs.
  • The identifier study shows that names carry intent for summarization and affect execution prediction. It did not test repository search or the naming rules proposed here.
  • CodeCompass evaluated structural file discovery on one FastAPI repository. Its 99.4% result is required-file coverage on hidden-dependency tasks, not implementation pass rate.
  • SlopCodeBench measures verbosity and concentrated complexity. Those are useful signals, not a complete definition of maintainability.
  • None of the studies evaluated this article’s combined instructions, signposts, review pass, and metrics as one system.

That leaves room for a useful engineering synthesis, but not for certainty. Apply the rules where they make the repository easier to explain, then measure whether agents actually search less, revisit less, and preserve the boundaries over repeated work.

Conclusion

Code is an interface for the next agent and the next human. Names expose intent to search. Boundaries determine whether a concept has one discoverable home or several arbitrary fragments. Explicit composition points reveal relationships that lexical search would otherwise miss.

The honest thesis is modest: navigable code can reduce operational waste and repeated navigation, but it does not make a coding agent correct. Correctness still depends on specifications, strong oracles, tests, review, and a harness that records evidence rather than trusting the final diff.

So optimize the route without confusing it for the destination. Give behavior names people will search for. Extract boundaries that compress knowledge rather than scatter it. Signpost structural dependencies. Review the change surface, not only the implementation. Then watch what happens over the next series of changes, because agents can erode the structure you gave them.

Once the code provides a usable navigation surface, surround the agent with a system capable of verifying what it does there. That is the role of a reliable coding-agent harness.

References

  • Trivedi, P., & Schmitt, O. (2026). Does Code Cleanliness Affect Coding Agents? A Controlled Minimal-Pair Study. arXiv preprint. arXiv:2605.20049.
  • Le, C. C., Pham, M. V. T., Van, C. D., Phan, H. N., Phan, H. N., & Nguyen, T. N. (2025). When Names Disappear: Revealing What LLMs Actually Understand About Code. arXiv preprint. arXiv:2510.03178.
  • Paipuru, T. (2026). CodeCompass: Navigating the Navigation Paradox in Agentic Code Intelligence. arXiv preprint. arXiv:2602.20048.
  • Orlanski, G., Roy, D., Yun, A., Shin, C., Gu, A., Ge, A., Adila, D., Roberts, N., Sala, F., & Albarghouthi, A. (2026). SlopCodeBench: Benchmarking How Coding Agents Degrade Over Long-Horizon Iterative Tasks. arXiv preprint, version 2. arXiv:2603.24755.
  • Bai, L., Huang, Z., Wang, X., Sun, J., Mihalcea, R., Brynjolfsson, E., Pentland, A., & Pei, J. (2026). How Do AI Agents Spend Your Money? Analyzing and Predicting Token Consumption in Agentic Coding Tasks. arXiv preprint. arXiv:2604.22750.

This article, images or code examples may have been refined, modified, reviewed, or initially created using Generative AI with the help of LM Studio, Ollama and local models.