88% of my coding agent's usage goes on reading

2026-10-25

Token-saving tools shrink what the model writes. I measured 35 days of my own sessions: what it writes is 12%. Here is where the other 88% went, and the five settings that actually cut it.

There is a whole genre of tools for making coding agents cheaper, and almost all of them go after the same thing: the words the model writes. Make it answer in fewer sentences. Make it talk like a caveman (there is a skill for that, and it is called caveman). Compress the code it prints. Filter the noise out of shell output before the model sees it.

I run Claude Code most of the day, with agents working in the background while I do other things, so I care about this a lot. Before trying any of it, I wanted to know one boring thing: what am I actually paying for?

So I went through 35 days of my own session transcripts and the client's usage stats and split everything into two piles: what the model wrote, and what it read.

What it wrote, every reply and every line of code, came to 12%.

The other 88% was reading. The system prompt, the tool definitions, the files it opened, and the whole conversation so far, sent back to the model on every single request. Most of that is a re-send of context it has already seen. The rest is new context going into the cache for the first time.

Every tool in that genre was fighting over the 12%.

Why reading is the expensive part

A chat with a model is stateless. Each time the agent takes a step (read a file, run a command, think, reply), the client sends the model everything again: instructions, tools, the history, the tool results. Prompt caching makes the repeated part much cheaper per token, but "much cheaper" times "every step" times "hundreds of steps a session" is still the biggest number on the page.

Step 1system + tools + your promptreplyStep 2all of step 1, re-senttool resultreplyStep 3all of steps 1 and 2, re-senttool resultreplyre-sent context (cache read)new context (cache write)output

Three steps of one session, drawn to show the shape rather than to scale. Every request re-sends everything before it. The output is the short grey end of each bar.

Two weeks after the first measurement I ran a finer one, 7 days and 11.7k requests, split by the three token types that actually get billed differently:

Cache read 50%
Cache write 35%
Output 16%

Share of usage, 7 days, 11.7k requests. The grey sliver on the right is the part everyone optimises.

Cache reads are the context being re-sent and served from cache: cheap per token, paid on every turn. Cache writes are context going into the cache: the most expensive token type there is, per token. Output is everything the model generated.

(Output came out at 16% here and 12% over the 35 days. Different window, different mix of work. Either way it is the small slice.)

The context also gets big. My median session peaked at 95k tokens, and one session in ten went past 266k. Near the peak, every step rereads all of it, including detours from an hour ago that have nothing to do with what the agent is doing now.

First, the suspects I could clear

Shell output. This is what most filtering tools target: ANSI colour codes, progress bars redrawn with carriage returns, the same line repeated over and over by a noisy build. I counted all of it across my agents' shell output. It was 0.72% of that output. Shell results were only 4.7% of all tool-result text in the first place. The client already pages large file reads (at 25k tokens), refuses giant ones, and swaps unchanged re-reads for a short stub. There was almost nothing left for a filter to take.

The bigger tool-result cost was file reads: results longer than 10k characters made up 20.5% of all tool-result text. That is a habit problem (read the function, not the whole file), and no output filter touches it.

Third-party token savers. I didn't test these myself; independent comparisons already have (for example this one). Most popular tools landed within about ±7% on total cost. A couple made it worse: one shell-output rewriter came out around +7%, and one context-management tool around +52%. If a tool says it saves 60%, that is usually 60% of the slice it touches. Of a 12% slice, that is about 7% of the bill, before you count the tokens its own instructions add to every request.

I'll admit this part was a little deflating. I wanted a clever fix. The real fixes were all settings.

I also have a bias here, and I'd rather say it than hide it: I'd almost always rather change a setting than install a tool. A tool comes with its own instructions, its own updates and its own claims about how much it saves, and its instructions are more context sent on every request. On a bill that is 88% context, a tool that adds context has to work very hard just to break even.

What actually moved it

All five are about context. None of them make the model say less.

1. Stop sending tools nobody calls

Every request carries the full definition of every tool the agent might use: name, description, JSON schema. Some of those schemas are long, and I never use the tools in personal projects. Claude Code lets a plugin mark a tool as deferred, so only its name is sent and the schema loads if the model actually asks for it. I wrote a small one for the handful of tools I don't use.

Result: 13.7k tokens off every request. Not every session. Every request. Over a week of 11.7k requests, that one change is worth more than any amount of terser replies.

2. List unused skills by name only

Skills are the same problem in a different coat. Each installed skill puts its name and description in the system prompt so the model knows it exists. I had 51 skills I hadn't used in 30 days. One setting keeps them installed and loadable but drops the description:

"skillOverrides": {
  "anthropic-skills:canvas-design": "name-only",
  "anthropic-skills:chrome-browser": "name-only",
  ...51 of these
}

The least exciting JSON I have ever been happy about.

Together with denying three tools I never used, the first request of a session in an empty directory went from 25.0k to 18.7k tokens, measured with claude -p before and after.

3. Compact earlier

Compaction summarises the conversation and starts a fresh context from the summary. By default it waits until the context is huge. I lowered the window from 300k to 200k (and later 150k). On my own transcripts, a simulation of the first step said about 18% fewer context tokens, at the cost of roughly 3.4 extra compactions a day. A summary loses detail, so this is a trade, and I'm still not sure 150k is the right number for long debugging sessions. For the way I work, it has been fine.

4. Compact before the cache expires

This one surprised me, and it gets its own section below.

5. Put the helpers on a cheaper model

General-purpose subagents (the ones an agent spawns to go search or summarise) were 15% of a day's usage, all running on the top model. One environment variable, CLAUDE_CODE_SUBAGENT_MODEL=sonnet, moves them to a cheaper one. Forks, which copy the parent's whole context, still inherit the parent's model, which is what you want.

The hidden one: full rewrites

Here is the part I didn't see coming.

The cache has a lifetime. If a session sits idle for more than an hour, the cache entry expires, and the next message has to write the entire context back into the cache. That is a whole session's worth of the most expensive token type, all at once, for typing "ok, continue".

When I counted, 16% of my usage was these full rewrites. The causes I could do something about:

The fix for the idle case already existed: Claude Code can compact an idle session at about 54 minutes, before the cache expires, so coming back rewrites a short summary instead of 150k tokens.

Then I read how it decides. The default only compacts sessions bigger than 200k tokens. I had just set my compaction window to 150k, so no session ever got that big, so idle compaction could never fire. Two good settings, silently cancelling each other out. Setting CLAUDE_CODE_IDLE_COMPACT_MIN_TOKENS=100000 (the lowest it accepts) fixed it.

I only caught that because I was looking at full rewrites as their own number. If you only look at how long the replies are, none of this exists.

A side effect: it changed how I build tools

Once you see the bill this way, you start noticing context re-sends everywhere. I have a small prompt-tidying helper that rewrites my rough prompts before they're sent. Its first version forked the current session to do it, which meant re-reading the entire conversation for every tidy. The rewrite sends only my last prompt and the last reply to a cheaper model, a tiny fraction of the reading.

What I'd tell you to do

Before you install a token saver, split your usage three ways: cache reads, cache writes, output. If output is a small slice (for agent work it almost always is), shorter answers can't save you much, and the tool's own instructions might cost more than it saves.

Then go after what gets sent on every turn: tool schemas you never call, skill descriptions you never read, stale context that should have been compacted, and the full rewrites when a cache expires. Check that your settings don't cancel each other out.

Measure first. Then cut the 88%.


That's it for this one. Next up: the steps I pulled out of the model loop entirely and turned into plain scripts, and why my agents got more reliable when they were allowed to do less. Follow along on RSS or X @denzilnelsonai. If you've measured your own split and got a very different number, I'd like to hear how your work differs.

— Denzil Nelson