Chapter 5 of 7 · 9 min read
Halving a file finds the duplicate, the obsolete and the prose
On this page
A platform lead inherits a CLAUDE.md of 2,400 tokens and a mandate to get it under 1,200 by Friday. The mandate came from a director who read a blog post, which is a poor reason and produces a useful exercise anyway. The team's position, stated in the first five minutes of the meeting, is that every line was added deliberately, each in response to a real problem, and that none of them can go.
That position is sincere and it is also unfalsifiable as stated, so the way through is to stop discussing the file as a whole and inventory it. Counting each section with the method from chapter 4 takes ten minutes and turns an argument about principle into an argument about six specific things.
| Section | Tokens | Verdict | Reason |
|---|---|---|---|
| Build and test commands | 210 | Keep | Executable verbatim; used on nearly every task |
| Architecture, three paragraphs | 620 | Shrink to 140 | Most of it is derivable from the directory tree |
| Two invariants | 130 | Keep | Wide reach, no expiry, checkable |
Exception for the pricing-v1 service | 180 | Cut | The service was deleted in November |
| Code-review checklist | 540 | Cut | Verbatim duplicate of the PR template |
| How to write a good commit message | 720 | Cut | Restates behaviour the model already produces |
| Total | 2,400 | → 1,120 |
Nothing in that table is a negotiation. Four of the six verdicts follow from facts anyone can check in a terminal, and the fifth is a measurement. The order in which they get cut is what makes the exercise repeatable, and it is not by length.
Cut what is false, then what is duplicated, then what is inert
Start with entries that are no longer true, because they are the only category that is actively harmful and the only one nobody will defend once the check is run. The pricing-v1 exception reads pricing-v1 is exempt from the shared error envelope; do not "fix" its bare 500 responses. One command settles it:
$ git log --oneline --diff-filter=D -1 -- services/pricing-v1
8f21c04 remove pricing-v1 after migration to pricing (#4471)
$ git show -s --format=%ci 8f21c04
2025-11-19 14:02:11 +0000The service has not existed for nine months. Until this morning, every session on this repository carried 180 tokens instructing the agent about the error-handling exemptions of a directory that is not there. That is not merely waste; an agent asked to audit error envelopes across services now has a reason to look for something that does not exist, and it will spend a turn or two doing so before concluding the file is wrong — which is a conclusion agents are reluctant to reach about their own instructions.
Duplicates go next. The 540-token code-review checklist is the same fourteen bullets that live in .github/pull_request_template.md, and the tempting defence is that duplication makes the agent aware of the standard earlier. Two things undercut it. The checklist applies during review, which is a narrow workflow, so it fails the reach test outright — an agent renaming a column cannot violate a review checklist. And a duplicated policy has two copies with one owner, so the copies drift; the PR template gained a bullet about feature-flag cleanup in February and the config file's copy did not, which means the agent has been working from a version of the standard the team stopped using. The template is where the checklist is read at the moment it applies, so the template wins and the copy goes.
Finding duplicates is easier than it sounds, because the ones that matter are near-verbatim rather than conceptual. The config file's content was written by copying from somewhere, so the overlap is usually literal enough that a line-level comparison against the repository's other prose finds it: run each non-trivial line of the config against the PR template, the contributing guide, the ADR directory — the architecture decision records, one short document per significant technical choice and the reasoning behind it — and the service READMEs, then read anything that matches. On this repository that turned up the review checklist immediately, along with a smaller find — four lines about branch naming that also live in the contributing guide, with the config file's copy still describing a feature/ prefix the team abandoned. The conceptual duplicates, where two documents say the same thing in different words, are not worth hunting; they are rare and the effort of finding them exceeds what they cost.
The commit-message section is the largest single item and the one most likely to be defended on quality grounds, since it is well written and describes a standard the team genuinely holds. It is also 720 tokens telling a model to write a short imperative subject line, wrap the body at 72 characters, and explain why rather than what — which is what the model does unprompted, because that is what the training distribution rewards. The way to settle it is chapter 4's method rather than assertion: twenty tasks, three runs each, with and without. This team ran it and got twenty subject lines under 50 characters in both arms, imperative mood in both arms, and one difference — the section asks for a Refs: PROJ-1234 trailer, meaning a Key: value line at the foot of the commit message, of the same form as the Co-authored-by: line tooling already writes there — which appeared in 18 of 20 runs with the section and 0 of 20 without. So 40 tokens of that section were doing work and 680 were not. The 40 tokens stay, as a one-line entry naming the trailer format. That is the general shape: an inert section usually has one live sentence buried in it, and the measurement finds it.
Architecture shrinks to what the repository cannot say
The three architecture paragraphs are the interesting cut, because unlike the others they are neither false nor duplicated nor inert. They describe the service topology, and an agent that reads them is genuinely better oriented. The problem is that most of what they contain is visible to the agent for the cost of one directory listing.
## Architecture (before, 620 tokens)
The platform is a monorepo containing eight services under `services/`. The
`gateway` service is the public entry point and routes to `billing`,
`pricing`, `notifications`, and `accounts`. Each service is a Fastify app
with its own `package.json`, its own Dockerfile, and a `src/` directory
containing `routes/`, `domain/`, and `db/`. Shared code lives in
`packages/`, of which the most important are `packages/http` (the shared
client and error envelope) and `packages/telemetry`...Every sentence of that is recoverable from ls services/, one package.json, and one glance at a service directory. The agent will do exactly that on its first read of any unfamiliar service, at a cost of two or three file reads, and it will get a more accurate answer than the paragraph, which was written when there were six services. What survives is the part the filesystem does not encode:
## Architecture (after, 140 tokens)
- `gateway` is the only service with public ingress; everything else is
reachable only from inside the cluster.
- `packages/http` owns the error envelope. A service that hand-rolls an
error response is a bug, not a style choice.
- `accounts` is being split into `accounts` and `identity`. New auth code
goes in `identity`; do not add to `accounts/src/domain/auth/`.Those three facts are invisible to a reader of the repository. The first is a security boundary enforced by network policy in a different repo. The second is an intent that the code does not announce. The third is a direction of travel — the kind of fact that is only true for a quarter, which is exactly the shape chapter 2 sent to nowhere, and it survives here because the team is willing to name an owner and a removal date for it. That willingness is what separates a legitimate temporary entry from a sprint goal: somebody's name is on the line, in the file, with the date the split lands.
The runbook used two days a month
The remaining item is the one the earlier chapters have been circling: a 700-token deploy runbook, used on the two days a month somebody deploys. Cut it, move it, or keep it.
By reach it does not belong in an always-loaded file, and chapter 1 settles that part without further argument. By chapter 3's rule, moving it into a skill is only a real answer if somebody runs the firing check on the requests it used to cover, which for a deploy runbook means the phrasings people use when they are deploying under pressure. So the decision is a move, conditional on a named person owning a measurement due within a week of the move.
If nobody will own that check, keeping the runbook where it is remains the more honest option, and the halving target has to be met somewhere else. That sounds like a concession and it is the point: a move without verification is a deletion that the team has not admitted to, and it is worse than either a clean deletion or an expensive keep, because it produces false confidence about content that is no longer reaching the agent. Between the two honest options, this team moved it and assigned the check, and the check came back at 31 of 38 after one description rewrite.
Then there is the entry someone insists on keeping and you believe does nothing. Arguing about it directly does not work, because you cannot prove a negative in a meeting and the person who added it can always name a hypothetical task where it might help. Three moves work better than argument. Measure it, using chapter 4's protocol, and let the result speak; a null result does not prove uselessness, but it does move the burden. Convert it into something checkable, since half the time the entry is a vague version of a rule that a lint or a script could enforce outright, and the person defending it usually prefers the stronger form. And if it survives both, attach a date and an owner to it and revisit then — not as a bureaucratic ritual, but because the file's real problem is that nothing in it ever gets a second look, and an entry with a review date is the only kind that does.
A halving target is a diagnostic, not a policy
The exercise worked here, and it is worth being clear about why, because the wrong lesson is that config files should be halved periodically. The target had no basis; 1,200 tokens is not a threshold anything measures against. What the target did was force an inventory, and the inventory found a dead exception, a drifted duplicate, and 680 tokens of prose restating a default. Those three findings were available at any time to anyone who looked; the arbitrary deadline is what made someone look.
A recurring halving mandate would do damage, because the second pass has nothing obvious left and starts cutting things that are load-bearing to hit a number. The commands and the invariants are the file's actual content, and a team that cuts them to reach 600 tokens has optimised the metric. The useful version of this as a standing practice is smaller and duller: check the file against the repository once a quarter — do the services it names still exist, do the commands it prints still run, are the duplicated policies still identical — and let it be whatever size it is when those checks pass.
Which leaves the entries that no inventory dislodges: the ones added in the week after an incident, defended by people who remember the incident, and sized in proportion to how bad the incident was. The largest of those on this team is 900 tokens of security policy that a director wants in every repository, and it is going to fail every test in this topic while being, in an important sense, a response to a real problem.
What belongs in a config file — read the current version at https://daviddominguez.dev/learn/en/what-belongs-in-a-config-file/halving-a-file-finds-the-duplicate-the-obsolete-and-the-prose