# I measured 8 weeks of Claude Code: 87% lower cost, 86% first-try typecheck

> Source: <https://dev.to/diegoprieri/i-measured-8-weeks-of-claude-code-87-lower-cost-86-first-try-typecheck-gp2>
> Published: 2026-11-03 16:20:53+00:00

Everyone has an opinion about coding agents: they're too expensive, or they're magic, or the code they write is garbage. I lead the frontend of a ~120k-line TypeScript app where agents write most of the code, so I wanted numbers instead of opinions.

It turns out the data was already on my machine.

Claude Code writes every session to `~/.claude/projects/**/*.jsonl`. Each line is an event, and two kinds of event matter here:

`usage` block with token counts: uncached input, cache writes (split by 5-minute and 1-hour TTL), cache reads and output.`Edit`, `Write` and `Bash` command the agent ran, with its output.
That's enough to answer two questions:

I wrote a small CLI, [claude-code-metrics](https://github.com/diego-prieri/claude-code-metrics), that reads those files and answers both. Nothing leaves your machine.

```
npx tsx src/cli.ts                     # everything
npx tsx src/cli.ts --since 2026-10-01  # a period
npx tsx src/cli.ts --project my-app    # sessions whose cwd contains "my-app"
```

Over 8 weeks and 7.5B input tokens:

|  | Value | 
|---|---|
| Input tokens read from cache | **98.4%** | 
| Input cost vs. no caching | **87.4% lower** | 
| Responses | 30,439 | 

Prompt caching changes the price of each input token, depending on where it comes from:

| Token type | Price vs. base input | 
|---|---|
| Uncached input | 1× | 
| Cache write, 5-minute TTL | 1.25× | 
| Cache write, 1-hour TTL | 2× | 
| Cache read | 0.1× | 

So the savings are:

```
savings = 1 − actual / uncached
actual   = input×1 + write5m×1.25 + write1h×2 + read×0.1
uncached = (input + write5m + write1h + read) × 1
```

The base price cancels out, so the ratio holds for any model. In code it's a few lines:

``` js
export const MULTIPLIERS = { input: 1, write5m: 1.25, write1h: 2, read: 0.1 };

export function ratios(t: TokenTotals): CacheRatios {
  const uncached = t.input + t.write5m + t.write1h + t.read;
  if (!uncached) return { readShare: 0, savings: 0 };
  const actual =
    t.input * MULTIPLIERS.input +
    t.write5m * MULTIPLIERS.write5m +
    t.write1h * MULTIPLIERS.write1h +
    t.read * MULTIPLIERS.read;
  return { readShare: t.read / uncached, savings: 1 - actual / uncached };
}
```

One gotcha: Claude Code logs **one line per content block**, all with the same message id. If you don't deduplicate by id, you count the same response several times.

An agent session re-sends a long, mostly stable prefix on every turn: system prompt, tools, project instructions, the conversation so far. Only the tail changes. That's the ideal case for caching: you pay to write the prefix once, then read it at 10% of the price for the rest of the session.

This is the number I care about most, because it measures how good the agent's first attempt is, not how good it gets after five retries.

| Check | Attempts | Passed on first run | 
|---|---|---|
| Typecheck | 205 | **86.3%** | 
| Tests | 298 | **75.2%** | 

Each session is replayed in order:

`MultiEdit`) to a matching file `.ts/.tsx/.vue` arms typecheck, code files arm tests.`tsc`, `jest`, `vitest`, `npm test`, …) is the `error TS…`, `N failed`, `not ok`).
Agents love to pipe long output through `head`, `grep` or `tail`, and a filtered output can hide the failing line. So the CLI also reports the rate on **unfiltered runs only**:

| Check | Unfiltered runs | Pass rate | 
|---|---|---|
| Typecheck | 10 | 100% | 
| Tests | 21 | 52.4% | 

The samples are small, but the test number is a warning: treat 75% as an **upper bound**. This is also why, in my workflow, the agent never commits without a gate that re-runs typecheck and tests with full output.

The code is MIT-licensed: [github.com/diego-prieri/claude-code-metrics](https://github.com/diego-prieri/claude-code-metrics). I'd love to see other people's numbers. If you run it, share yours in the comments.
