For the third time this afternoon, the agent edits the wrong file.
You asked it to change how prices are rounded. It opened formatPrice.ts, looked confident, and wrote a clean little diff in the display formatter. The change belonged in the tax engine, where rounding actually happens. You point it at the right file. Twenty minutes later, a new task, the same class of mistake: a change that should live in one place, smeared across four.
The easy story is that the model wasn’t paying attention. It was. It read your code exactly as written, and your code does not say where rounding lives. To answer “where does this number get decided,” the agent - and any new engineer - has to open formatPrice.ts, follow it into priceUtils.ts, which calls taxRules.ts, which imports a constant from config/regions.ts. Four files to trust one concept. The agent guessed. It guessed the way the structure invited it to.
Most of what this site covers is context you add on top of the code: a rules file, an MCP connection, a skill. All of that is real, and none of it decided which file rounding lives in. The largest and most permanent context an agent reads is the one nobody thinks to add, because it is already there: the shape of the code itself. Usually you close the agent’s knowledge gap by supplying a map in prose. This post works the other lever: change the territory so it needs less map.
That’s not an attention bug. It’s an interface bug, and it has a fix with a name and a price on it.
First, check whether you have this
Section titled “First, check whether you have this”Don’t take the numbers below on trust. Thirty seconds on your own repo settles it.
Pick one concept your codebase decides somewhere - rounding, permissions, retry timing, whatever comes to mind first. Open a fresh agent session and ask it one question: “where does this get decided, and why?” Don’t let it touch code. Just count how many files it has to open before it can answer with confidence.
One file, clean answer: your boundaries are already deep enough, and the rest of this post has nothing for you. Close the tab. Three or four files, with the import chain doing the answering: you have the tax this post is about, you pay it on every question shaped like this one, and the code will never make it stop.
The tension this post turns on: shallow structure charges every future reader the full multi-file price, on every ask, forever, because nothing in the code holds the answer. Deep structure pays a designer’s price once and hands every later reader a single file to check. Someone pays either way. The only question is how many times.
Here is where the rest of this piece ends up, so you know what you are buying. Collapsing a spread-out decision into one small interface costs about three passes through the tangle to draw: 16,400 tokens, against the 5,500 a single pass costs. It pays for itself by the fourth time anyone, agent or teammate, asks where the decision lives, because every read after that costs about 7.9x less than groping through the tangle it replaced.
The self-test is free. Everything from here has a price, and the next section prices it.
The tangle, priced
Section titled “The tangle, priced”Fix one running example and keep it for the rest of the piece: the rounding tangle from the top. Four files, each declared at a size any one of them looks perfectly reasonable at, 90 to 140 lines apiece. Fourteen call sites across the codebase reach into them directly. All of that is normal. A slightly-too-organized Tuesday.
Count what one “where does this decide rounding” pass costs, computed the obvious way, before any fix exists:
| What the reader does | Count | Tokens each | Subtotal |
|---|---|---|---|
Searches (grep, ~40 lines of hits) | 3 | 300 | 900 |
| File reads (~110 lines each) | 4 | 900 | 3,600 |
| Reasoning steps connecting the chain | 5 | 200 | 1,000 |
| One pass through the tangle | 5,500 |
Three greps, for “round,” “price,” and “tax.” Four files read in full. Five reasoning steps to connect formatPrice.ts to the constant buried in config/regions.ts. 5,500 tokens, to answer a question that starts with the word “where.” Before a single line changes.
Here is the part that should bother you: the second pass costs the same. There is no cache to hit. A fresh session next Tuesday, a new teammate, next month’s version of you - each one reopens all four files and reasons the chain from scratch, because nothing in the code says the answer out loud. Every future ask pays the 5,500 again.
Now the fix, which is old enough to have a name. A deep module, Ousterhout’s term from A Philosophy of Software Design, is a small interface in front of a large implementation. Unix file I/O is the canonical case: five calls, open, read, write, seek, close, in front of decades of on-disk layout and caching that changed underneath them without the five signatures ever moving. A shallow module is the opposite: a wrapper whose interface is nearly as big as what it wraps, so you pay the cost of reading it and get no hiding in return. The reflex that produces shallow piles has a name too: classitis, chopping one coherent thing into pass-through pieces and calling the result organization. The four pricing files are four shallow modules pretending to be a design.
Collapse them into one deep module - a 25-line pricing.ts whose whole surface is pricing.quote and pricing.format - and the identical question costs one grep to locate the file, one read of the interface, one step to confirm that’s the whole answer. That is 300 + 200 + 200 = 700 tokens.
5,500 against 700 is 7.857x. Call it 7.9x, and remember it: that ratio is the per-read discount, and everything from here multiplies it out.
This fix is easy to mistake for a smaller one. A mined glossary gets everyone using the same word for the concept, and that is worth having. But four files can agree perfectly on vocabulary and still fight over which of them owns the decision. Naming a concept and owning it are different fixes for different symptoms, and this post is about the second one.
The tangle charges 5,500 tokens for a question the deep module answers for 700, and it charges the 5,500 every single time.
Draw it three times
Section titled “Draw it three times”So collapse the tangle. The catch is the step where you decide what the collapsed interface looks like, because today’s agents are good at writing implementations and bad at deciding where a boundary sits. That asymmetry stopped being a contrarian claim a while ago; by 2026 it is standard framing, and industry pieces now sell architecture in these terms: fewer tokens per task, fewer pull requests bounced for incoherence. Ask an agent to “build a pricing service” and it will happily generate ten files that import each other in a knot. Competent code, wrong shape. Drawing the seam is a judgment call, and one agent asked to make it alone returns a single answer with its assumptions hidden.
So don’t ask for one design. Ask for three, in parallel, each optimizing a different axis of the same interface: the smallest surface that does the job; a version with a few extra seams for likely future callers; one shaped exactly around how today’s fourteen call sites already use the code. You read three competing surfaces side by side and pick. The judgment call an agent can’t make alone, it can hand you cheaply, because the three run at once.
Call the move draw it three times. The seam still gets drawn once, but by three competing proposals instead of one blind guess, and your judgment goes to choosing between designs instead of inventing one from nothing.
Package it as a skill so it is repeatable rather than a one-off:
---name: improve-codebase-architecturedescription: Find a tangle of shallow modules and propose a deep module to replace it.---
# Steps1. EXPLORE. A read-only subagent maps the tangle: every file, every call site, every place a concept the code decides gets guessed at.2. DESIGN. Three subagents, in parallel, each proposing a competing interface for the same deep module: minimal / flexible / caller-optimized.3. SYNTHESIZE. Compare the three. Pick or merge. Write a refactor RFC.4. EMIT. Open the RFC as an issue. Do NOT write implementation code yet.Each step is a subagent with its own scoped window, so the messy exploration, dozens of tool calls and dead-end reads, never pollutes the thread where you make the actual call. The whole workflow stops short of code on purpose. It emits an RFC and waits for a human to read it:
# RFC: Extract a deep `pricing` module
## ProblemRounding is decided across formatPrice.ts, priceUtils.ts, taxRules.ts,and config/regions.ts. 14 call sites reach into them directly.
## Proposed interface (caller-optimized) pricing.quote(item, region) -> Money pricing.format(money, locale) -> string
## Blast radius14 call sites. All currently reach into priceUtils directly; allcollapse to pricing.quote().Three competing interfaces cost barely more than one, and the judgment call turns from inventing a design into choosing one.
What it costs to draw the seam once
Section titled “What it costs to draw the seam once”The workflow isn’t free, and the ledger below would lie if it hid that. Price the four steps the way the skill runs them, one table:
| Step | What happens | Tokens |
|---|---|---|
| 1. Explore | Read-only subagent maps the tangle: 4 searches, 4 file reads, 14 call sites skimmed, 8 reasoning steps | 8,500 |
| 2. Design x3 | Three subagents in parallel, 1,900 each, proposing competing interfaces | 5,700 |
| 3. Synthesize | Compare the three, write the RFC | 2,000 |
| 4. Emit | Open the RFC as an issue | 200 |
| One-time refactor cost | 16,400 |
16,400 against a 5,500-token tangle pass is 2.982x: drawing the seam once costs about three trips through the tangle it is about to replace. That is the up-front bill.
Set it against the 700-token read of the finished interface, and the break-even sits exactly where the two lines cross: the one-time cost divided by what each read saves, 16,400 / (5,500 - 700) = 3.417 reads. Not ten. Not thirty. A little past the third time anyone asks where the decision lives.
Three tangle-passes to draw the seam, paid back on the fourth ask. The rest of this piece is that number run out to a table.
The ledger
Section titled “The ledger”| Reads | No refactor (tangle every time) | One deep module (refactor once + reads) | Saved |
|---|---|---|---|
| 1 | 5,500 | 17,100 | -11,600 |
| 2 | 11,000 | 17,800 | -6,800 |
| 3 | 16,500 | 18,500 | -2,000 |
| 4 | 22,000 | 19,200 | +2,800 |
| 5 | 27,500 | 19,900 | +7,600 |
| 10 | 55,000 | 23,400 | +31,600 |
| 20 | 110,000 | 30,400 | +79,600 |
This table is the picture to keep. Every section from here is a row of it.
The sign flips between rows 3 and 4. That is the 3.417 made visible: on the third ask you are still down 2,000 tokens, and by the fourth you are up 2,800. Every ask after that is the 7.9x from the pricing section, paid again and again.
One row this ledger doesn’t need, and the absence means something. A cached map, the throwaway kind this site has priced before, decays on a clock and needs a “stale, delete and regenerate” row, or it starts lying. A deep module has no freshness clock. It only decays if you let the two failure modes further down actually happen, and those are risks you design against, with no expiry date to track.
Twenty reads against the tangle is 110,000 tokens of pure rediscovery. Twenty reads against one deep module is 30,400, and 16,400 of that was spent exactly once.
The rule that keeps it deep
Section titled “The rule that keeps it deep”A refactor that isn’t written down decays back into a tangle the first time someone reaches around it under deadline pressure. Put the contract where the agent reads it every session, in your rules file:
## Deep modules- The INSIDE of a deep module is a gray box. You own the implementation; reviewers read the interface and the tests, not the internals.- Callers MUST go through the module's public interface. Never import from inside a module (e.g. never reach into pricing/internal/*).- When a concept spans 3+ files, stop and propose a deep module before writing more code. Tangle is a design smell, not a coding task.That gray box line is doing the real work. It splits ownership: your review covers the interface and its tests, and the 300 lines of internals stop being part of what you check. A pull request that only touches the inside of pricing.ts stays small to read for the same reason, because the interface and its tests are the contract. The agent gets the same benefit on its next task: it loads pricing.quote and pricing.format, sees the boundary, and the four-file hunt from the top of this piece just doesn’t happen.
The rules clause is what turns a one-time 16,400-token refactor into a standing 7.9x discount instead of a tangle that quietly regrows.
When a deep module is the wrong move
Section titled “When a deep module is the wrong move”Three ways this specific move backfires, run against the pricing example instead of left abstract.
The first is the false deep module. You fold genuinely unrelated logic behind pricing.quote because it happened to live nearby. Now a small interface is lying about how much is going on inside it, which is worse than the tangle: the tangle at least admitted it was four separate things. If you can’t name the single decision pricing makes, the seam is wrong.
The second is a sealed seam. You close off a boundary callers actually need. If some of the 14 call sites want pricing.quote(item, region) and others legitimately need to override the rounding mode per request, one rigid entry point forces them to fight the interface. They’ll reach into pricing/internal/* to get around it, and the gray-box rule from the last section becomes a rule nobody follows. When the variation is real, put it on the interface as a parameter. A deep module narrows what callers have to understand. It has no business narrowing what they are allowed to do.
The third is agent-specific. A deep module the agent can’t see into can hide its own bugs from review just as easily as it hides complexity from you. The gray-box deal only holds if the tests behind pricing.quote are real. Hand an agent a deep interface with thin tests and you have built a place for its mistakes to live where nobody is looking. Depth buys a smaller map only if something is actually guarding the territory you stopped reading.
All three failures share one tell: the interface stops describing one decision. Check that before you check anything else.
What this doesn’t solve
Section titled “What this doesn’t solve”The workflow above isn’t this site’s invention, and shipping it without saying so would be passing off someone else’s popular idea as original. A public skill under the identical name, improve-codebase-architecture, already ships in mattpocock/skills and runs the same explore, design, synthesize, RFC shape. As of 2026-08-10 it reports 666,000 installs on Skillselion. Borrow more than the name from it: the skill centers a sharper diagnostic than anything above, the deletion test. Imagine deleting the module entirely. If the complexity vanishes, it was a pass-through and never earned an interface. If it reappears scattered across every caller, the depth was real. Run that test on pricing before you trust the RFC.
There is also a second camp attacking the identical rediscovery cost without touching the code’s shape at all. Tree-sitter and AST knowledge-graph servers over MCP, with named tools like CodeGraph, GitNexus, and grepai, index the codebase and answer “where does this decide it” against the index instead of the files. Independent reports from 2026 put the saving at roughly 10x fewer tokens and 2 to 2.1x fewer tool calls. The trade, stated plainly: an index is faster to adopt and doesn’t require your team to agree on where the seams go, but it is a bolt-on that must be kept in sync with the code, and it does nothing for the next human who opens formatPrice.ts in an editor with no MCP server running. A deep module changes the artifact itself, helps that human too, and has no separate freshness clock to maintain, at the cost of being harder to ship: 16,400 tokens and a pull request a human has to review, where an index builds itself in the background.
“Deep module” and “design it twice” aren’t this piece’s invention either. Both are Ousterhout’s, from A Philosophy of Software Design (2018). Running three parallel subagents instead of designing twice yourself is the one thing this piece adds to that book: a third opinion that used to cost a second afternoon now costs nothing but wall-clock time, because the three run at once.
And the anti-hype, because a ledger with a win at the end is exactly where it’s earned: the numbers above are a toy for one four-file slice. A real refactor also costs review time, and a bad seam, the three failures above, can cost more than the tangle ever did.
One more thing handed forward rather than solved: depth tells you what to trust once you are in the right file. On a codebase with three hundred modules, it does nothing to tell you which of them to open first. That’s a discovery problem, and boundaries don’t answer it.
Borrow the deletion test, credit where the shape came from, and know that an index is the cheaper answer to the same question when you’re not ready to touch the code.
Back to the ledger
Section titled “Back to the ledger”The sign flip is the whole argument. The seam cost three tangle-passes to draw and paid for itself on the fourth ask, and every ask since has been collecting the 7.9x. That is the claim from the top of this piece, reconciled: both numbers, the three passes and the 7.9x, sit in the same table.
There is a cheaper, weaker version of this move: write the map down instead of redrawing the territory. A directory-scoped rules paragraph that says “rounding lives in pricing.ts” costs a paragraph, where the seam cost 16,400 tokens, and it’s the same write-the-map lever the directory-scoped rules post carries further. It also decays the moment someone adds a fifth file and forgets to update the paragraph, which the interface structurally can’t do: a caller that reaches past pricing.quote breaks at the boundary instead of drifting silently.
And the ledger above assumes you already know which of your modules to open. On a codebase with three hundred of them, “which file” is the search problem this site has already priced out twice: a disposable map when you’ll be back on this slice soon (cache the explore), or the same hunt handed to a cheap model instead of an expensive one, when you won’t (stop reading your codebase in the window). Once the file is known, a parameterized command that loads it deterministically is the fast path. Depth is what makes the right file trustworthy once you’re in it.
It was never going to be what gets you there.
About the numbers. All token figures in this post are toy numbers, invented for traceability and checked against a script before publishing, not measurements of any real repo. The per-unit prices (300 tokens per search, 200 per reasoning step, 900 per shallow file read, 200 for a deep module’s interface, 150 per call-site skim) are the same order of magnitude used elsewhere on this site, so the figures stay comparable across posts. The four-file, 14-call-site tangle is invented to be hand-checkable. The mattpocock/skills install count (666,000 on Skillselion, as of 2026-08-10) and the CodeGraph, GitNexus, and grepai token-reduction reports are the only claims quoted from outside sources; everything else is arithmetic that follows from the stated toy inputs.
For the per-tool mechanics, see Skills for the improve-codebase-architecture orchestration, Subagents for the parallel explore-and-design step, and Rules for the gray-box contract that keeps the interface from decaying.


