DEV Community

Cover image for How to Cut Claude Code Token Costs by 60% Without Losing Context (Practical Guide)
Dextra Labs
Dextra Labs

Posted on

How to Cut Claude Code Token Costs by 60% Without Losing Context (Practical Guide)

We were running Claude Code across a mid-sized TypeScript monorepo. Twelve engineers, active development across five services. The token bills were climbing fast, not because the team was being careless, but because nobody had thought carefully about context hygiene.

After spending two weeks systematically measuring and fixing token consumption, we cut the daily spend by 62% without any degradation in output quality. Most of the savings came from four things. This post covers all of them with before/after numbers.

What Actually Burns Your Tokens

Before fixing the problem, you need to understand where the tokens go.

Every Claude Code session has a context window, up to 200K tokens for Claude Sonnet models. But the cost isn't about the maximum. It's about what you're loading into every interaction.

Three things inflate context silently:

Your CLAUDE.md

It's loaded at the start of every session. If it's verbose, every interaction pays for that verbosity upfront.

File references

Every file you add to context stays in context. Loading broad directories when you need specific files is expensive fast.

Conversation history

Long sessions accumulate. By turn 30 of a debugging session, you may be paying for thousands of tokens of context that are no longer relevant to the current question.

The fix for each is specific. Let's go through them.

Fix 1: CLAUDE.md Surgery

Most CLAUDE.md files we've audited have the same problem, they were written incrementally, with something new added every time someone thought of a useful constraint. After a few months, they read like a junior developer trying to explain the entire codebase from scratch at the start of every session.

This is the pattern we commonly see:

# Project Overview
This is an e-commerce platform built in 2019 using Next.js, TypeScript, 
and PostgreSQL. We use React for the frontend, Express for the backend, 
Redis for caching, Stripe for payments, SendGrid for email, AWS S3 for 
file storage, Algolia for product search, and we follow a microservices 
architecture. Our database has 45 tables including users, products, orders, 
inventory, shipping, payments, reviews, categories, tags, suppliers...
[continues for another 2,000 tokens]
Enter fullscreen mode Exit fullscreen mode

The problem: 90% of that context is irrelevant to the task in front of Claude at any given moment.

The fix is to strip CLAUDE.md down to what Claude actually uses in every interaction, the hard constraints that apply across all tasks and move the rest to reference files that get loaded on demand.

# Stack: Next.js 14 / TypeScript / PostgreSQL 16
# Monorepo: packages/web, packages/api, services/

## Active constraints
- Repository pattern: no direct DB calls from API routes
- Stripe SDK v13 only, no direct API calls
- All types in /shared/types, no inline interface declarations
- Test coverage required for services/ changes

## Load when needed
- Architecture: /docs/architecture.md
- API contracts: /docs/api-contracts.md
- DB schema: /docs/schema.md
Enter fullscreen mode Exit fullscreen mode

Before: 3,200 tokens per session startup. After: 380 tokens per session startup.

That difference compounds across every single interaction in a session. On a typical 20-turn session, we were saving 55,000+ tokens just from the startup context reduction.

Fix 2: Selective File Loading

The second major source of token waste is file loading habits. Developers tend to add broad context out of habit, "load the entire services directory so Claude has the full picture", when most tasks only need two or three files.

The cost difference is not marginal. Loading a directory with 40 TypeScript files at an average of 200 lines each adds roughly 80,000 tokens to your context before you've written a single message. Loading the three files relevant to your task adds 6,000 tokens.

What we did instead:

Instead of loading broad directories, we started specifying exactly which files were needed for each task.

# Before: loading the entire payment service
@services/payment/

# After: loading only what's needed to fix the Stripe webhook handler
@services/payment/webhooks/stripe.ts
@services/payment/types/stripe.types.ts
@tests/payment/webhooks/stripe.test.ts
Enter fullscreen mode Exit fullscreen mode

For tasks that genuinely require broader context, refactoring across services, finding patterns across the codebase, we created task-specific context bundles that could be loaded together.

# Context bundle for auth-related work
@services/auth/
@packages/web/src/lib/auth.ts
@packages/api/middleware/auth.ts
@docs/auth-flow.md
Enter fullscreen mode Exit fullscreen mode

The bundle approach gives you the broad context when you actually need it, without loading it for tasks that don't.

Before: Average 45,000 tokens of file context per session. After: Average 11,000 tokens of file context per session.

Fix 3: Structure Your Prompts for Cache Hits

This one applies when you're using the Claude API directly alongside Claude Code, which most teams building AI-assisted workflows end up doing.

Claude's API supports prompt caching. Cached prompt portions cost roughly 10% of the standard input price. The catch: only content that appears consistently at the start of your prompt gets cached reliably.

The structural change that makes this work is separating stable context from dynamic context explicitly:

import anthropic

client = anthropic.Anthropic()

STABLE_SYSTEM_CONTEXT = """
You are a TypeScript engineer working on an e-commerce platform.
Stack: Next.js 14, PostgreSQL, Stripe v13, repository pattern.
Constraints: [your core constraints here]
"""

def get_completion(task_description, relevant_files):
    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=4096,
        system=[
            {
                "type": "text",
                "text": STABLE_SYSTEM_CONTEXT,
                "cache_control": {"type": "ephemeral"}
            },
            {
                "type": "text",
                "text": f"Current task context:\n{relevant_files}"
                # No cache_control here, this changes per request
            }
        ],
        messages=[{"role": "user", "content": task_description}]
    )
    return response
Enter fullscreen mode Exit fullscreen mode

The stable system context gets cached after the first call. Every subsequent call that hits that cache costs 10 cents per million tokens instead of the standard $3. For teams running hundreds of API calls per day against the same system context, this alone reduces the API cost significantly.

Cache hits compound with conversation length. A 20-turn session where the first 5,000 tokens are consistently cached looks very different on a cost dashboard than the same session without caching.

Fix 4: Session Management and When to Use /compact

Long sessions are expensive for a non-obvious reason: the entire conversation history stays in context for every new message. By turn 40, you're paying for everything from turn 1 forward, including the debugging rabbit holes you abandoned at turn 8 and the context that became irrelevant when you solved the first problem and moved to the second.

Two strategies here.

Compacting long sessions

Claude Code's /compact command summarizes the conversation history into a condensed representation. Run it when:

  • You're 20+ turns into a session and switching to a different task
  • You've solved one problem and are starting a new one in the same session
  • The conversation has accumulated significant exploratory context that's no longer relevant

The tradeoff is real: compacted history loses some nuance. Don't compact in the middle of a complex debugging session where earlier turns contain important error context. Do compact when you're pivoting to a new problem where the old context is just overhead.

Starting fresh sessions strategically

The common instinct is to keep sessions alive for continuity. The better approach is to keep session-specific notes in a scratch file and start fresh for distinct tasks.

# session-notes.md (gitignored)
## Task: Fix Stripe webhook signature validation
Status: Solved, issue was in middleware ordering, not validation logic
Relevant files: services/payment/webhooks/stripe.ts, middleware/raw-body.ts
Solution: raw-body middleware must run before body-parser
Enter fullscreen mode Exit fullscreen mode

A fresh session loaded with three relevant files and a one-paragraph task description in your CLAUDE.md is almost always cheaper than continuing a 30-turn session carrying all the context of the previous problem.

The Before/After Numbers

Here's what the full optimization set looked like across our client's engineering team over 30 days.

The output quality metric is the one that matters most. These weren't cuts that traded quality for cost, they were cuts that removed context that wasn't contributing to quality in the first place. Verbosity in your system prompt doesn't make Claude smarter about your codebase. Relevant, targeted context does.

The biggest single win was CLAUDE.md restructuring. It required one afternoon of cleanup and delivered persistent savings across every session from that point forward.

These techniques cut our token spend without degrading output. Context hygiene is not a one-time fix, it's a discipline. Once you build it into how your team works with Claude Code, the savings compound every day.

Full guide with measured before/after, the prompt caching implementation patterns, and the CLAUDE.md template we use across client deployments:

Claude Code Token Optimization - Full Guide

Dextra Labs helps engineering teams deploy and optimize Claude Code and enterprise LLM systems. If your token spend is climbing faster than your output value, it's usually a context architecture problem. hello@dextralabs.com

Top comments (0)