How to Write Navigable Code for Coding Agents

Before a coding agent edits a repository, it reads instructions, searches for symbols, follows imports, inspects tests, and builds a partial map of the change. The repository is therefore more than compiler input. It is an interface the agent has to navigate.

Clear names, meaningful boundaries, and visible dependencies can shorten that route, but the evidence does not support the broad claim that clean code makes agents more correct. In a controlled study, cleaner and messier versions of the same repositories produced similar pass rates: 91.3% and 92.1%. What changed was the operational footprint.

This article turns that result, plus research on identifiers, structural navigation, and long-running agent work, into practical rules for code. Its companion, How to Build a Reliable Coding-Agent Harness, covers the system around the model. Most sources here are recent preprints limited to particular models, harnesses, or repositories. I separate their measurements from my engineering interpretation because no study evaluated these practices as a complete package.

Code is a navigation interface

A task normally arrives in the language of behavior:

Normalize the search query before checking the result cache.

The agent must map that sentence to implementation using 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.

A five-line patch can require reading twenty files because the agent does not know which five matter. Navigable code makes the first useful search more likely to reach the right area, keeps related responsibilities easy to follow, and exposes non-obvious relationships before they become omissions.

This is why “put the whole repository in the context window” is incomplete. Capacity and discovery are different problems: a file that fits in context still contributes nothing 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 Java and Python repository pairs. 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%.

File revisitation is observable behavior, not a direct measure of confidence or confusion. It is still a useful proxy for an uncertain route because the agent returns to material it already inspected.

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 varied by up to 30 times in total tokens, while accuracy often peaked at an intermediate cost and then saturated. This does not show that navigable code eliminates variance, but it shows why avoidable navigation is worth removing.

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 capable of rejecting bad implementations. Navigable code reduces waste while the agent finds and inspects the change surface; it does not prove the patch is right.

Names are the retrieval interface

Names bridge a natural-language task and repository locations.

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

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

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

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

The When Names Disappear study 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 by different amounts. Identifiers carried intent that structure alone did not reliably recover.

The experiment evaluated summarization, not repository search, so it does not prove that normalizeSearchQuery lowers navigation cost. My engineering inference is that names which preserve intent give grep, symbol search, BM25, and model reasoning better anchors.

The rule is not “make every name long”:

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

Prefer vocabulary from requirements, public APIs, user-visible behavior, and the domain. Avoid private shorthand unless the codebase uses it universally. Use the same vocabulary in implementation, tests, configuration, and documentation so one search reveals a coherent path.

A searchable name attached to a mixed or misleading responsibility is worse than a plain one. Retrieval reaches the symbol; its boundary must still match the concept it advertises.

Structure that helps versus structure that scatters

“Make functions smaller” is not a sufficient rule. Smaller units can create precise search targets or scatter one task without adding 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%.

The case studies show both directions. In a 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 helpers became precise grep targets.

In a cluster job-limit task, helper extraction left the focal launch logic in place and spread surrounding work across more methods. Input tokens increased 8%, while most other metrics remained within a few percentage points of zero. The refactoring added surface without improving the route.

Use this rule:

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

Before splitting code, I use these questions instead of 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 one redistributes the same complexity across more places.

The dependencies code search cannot reveal

Names support semantic discovery when the task and code share vocabulary. Structural dependencies through imports, inheritance, instantiation, registration, events, or dependency injection may share almost no words with the request.

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

On the hidden-dependency tasks, vanilla navigation achieved 76.2% final required-file coverage, BM25 retrieval reached 78.2%, and graph navigation over imports, inheritance, and instantiation reached 99.4%. This measured coverage in one FastAPI repository, not implementation correctness. It exposes a limit of naming: semantic similarity cannot reveal every architectural relationship.

Codebases usually need two responses. At the harness level, use dependency graphs, call hierarchies, symbol references, and mandatory impact analysis, as described in the reliable harness article. In code, 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 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 instantiation searchable, the dependency object exposes constructor requirements, and the comment names the separate test composition root. Comments remain a fallback: “remember to update another file” will rot, while a useful signpost names the relationship and its exact symbol, registry, or composition root.

Agents erode structure, so guidance must be continuous

Repeated agent changes can make previously clear code harder to extend.

Version 2 of SlopCodeBench evaluated 15 coding agents on 36 problems with 196 sequential checkpoints. At each checkpoint, an agent extended its previous implementation under an evolving external specification. The benchmark tracked two limited dimensions: verbosity (redundant code) and structural erosion (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 up to 62.3% and verbosity by up to 34.8%. Average quality velocity still degraded about 1.3 percentage points per checkpoint. Across prompt strategies, cost per checkpoint increased 12.1% on average and aggregate correctness fell 2.3 percentage points.

Two metrics do not define maintainability, and this benchmark does not prove that every agent-assisted repository will degrade. It does show that a one-time “write clean code” prompt did not stop the measured drift. My engineering conclusion is that navigability must recur in implementation instructions, review criteria, and longitudinal observation.

How to direct agents to write navigable code

No cited study evaluated the following practices as a bundle. Treat them as engineering hypotheses to apply proportionally and inspect in your codebase.

1. Encode naming and boundary rules as instructions

“Write clean code” leaves the decisions undefined. I would adapt this starting point 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 turn an abstract preference into choices an implementation or review agent can inspect.

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. If all production construction of OrderRepository flows through createOrderRepository, a constructor task can find the composition root by searching for either symbol. Reflection based on a string in an unrelated file may provide no lexical route.

Do not centralize everything in a giant service locator. Prefer 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 take. In my experience, a focused review works better than another sentence in 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 may share the implementation agent’s blind spots, so it is not an independent oracle by default. This pass is useful because it asks a different question and demands concrete locations.

4. Enforce the smallest coherent change

“Smallest change” is easy to misread as “fewest changed lines.” The better target is the smallest coherent change: all required behavior behind the right existing boundary, without adjacent cleanup or abstractions for hypothetical requirements. This avoids 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.

When the current boundary cannot express the behavior safely, the smallest coherent change may include a refactor. Require the agent to explain which responsibility moves, what 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 shows why 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 outlier trajectories. Token count alone does not prove architecture quality.

No cited study shows how the per-task footprint effect of cleaner code compounds into long-horizon erosion in the same repositories. Monitoring that connection is a reasoned practice, not a proven formula.

What the evidence does not prove

The evidence has five central limitations:

  • 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.

Apply the rules where they make the repository easier to explain, then measure whether agents search less, revisit less, and preserve boundaries over repeated work.

Conclusion

Navigable code can reduce operational waste, but it cannot make an agent correct. Give behavior names people will search for, extract boundaries that compress knowledge, expose structural dependencies, and review the change surface over repeated work. Specifications, strong oracles, tests, review, and a reliable coding-agent harness must still verify the result.

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.