DEV Community

Saqueib Ansari
Saqueib Ansari

Posted on • Originally published at qcode.in

The Developer's Guide to Why Your Codebase Is Secretly Burning Claude Tokens

Every month, developers stare at their Anthropic invoices wondering where all their tokens went — the answer is almost always hiding in plain sight inside their own codebase.

Why Your Codebase Is Secretly Burning Claude Tokens (And You Don't Even Know It)

Most token waste isn't dramatic. It's not one rogue prompt that blows your budget. It's the slow, invisible hemorrhage caused by architectural decisions that made sense when you first wired Claude into your app but quietly compound into thousands of wasted tokens per day. Understanding why your codebase is secretly burning Claude tokens is the first step toward fixing it.

Let's break down the most common culprits and, more importantly, how to kill them.

The Context Window Overloading Problem

The single biggest offender in most codebases is context overloading — dumping everything into the prompt because it's easier than thinking carefully about what Claude actually needs.

Consider this pattern that shows up constantly in Laravel applications:

// The expensive anti-pattern
$response = $claude->messages()->create([
    'model' => 'claude-opus-4-5',
    'max_tokens' => 1024,
    'messages' => [
        [
            'role' => 'user',
            'content' => "Here is our entire product catalog: " . 
                         Product::all()->toJson() . 
                         "\n\nHere are all customer orders: " . 
                         Order::all()->toJson() . 
                         "\n\nNow answer this question: " . $userQuestion
        ]
    ]
]);
Enter fullscreen mode Exit fullscreen mode

That Product::all() call on a catalog with 500 products can easily add 40,000–80,000 tokens to every single request. If you're running 1,000 requests per day, you just burned 80 million tokens answering questions that probably only needed 200 rows of context.

The fix: Implement semantic retrieval. Use pgvector or a dedicated vector store like Pinecone or Weaviate to fetch only the top-k relevant records before building your prompt. A well-tuned RAG pipeline will typically reduce context size by 85–95% while improving answer quality because the model isn't wading through irrelevant noise.

// The efficient pattern
$relevantProducts = $vectorStore->similaritySearch($userQuestion, topK: 10);

$response = $claude->messages()->create([
    'model' => 'claude-opus-4-5',
    'max_tokens' => 1024,
    'messages' => [
        [
            'role' => 'user',
            'content' => "Relevant products:\n" . 
                         collect($relevantProducts)->toJson() . 
                         "\n\nQuestion: " . $userQuestion
        ]
    ]
]);
Enter fullscreen mode Exit fullscreen mode

System Prompt Bloat Is Draining Your Budget

This one is painful because it's usually caused by good intentions. Teams iterate on their system prompts, adding more instructions, more examples, more edge case handling — and before long you've got a 3,000-token system prompt being sent on every request, even the ones that only need 50 tokens to complete.

The Hidden Cost of Static System Prompts

In the Claude API, your system prompt counts toward your token bill on every call. A 3,000-token system prompt across 10,000 daily requests costs you 30 million tokens per day before Claude has read a single word of actual user input.

Audit your system prompt ruthlessly. Ask yourself:

  • Does every instruction apply to every request type?
  • Are you including few-shot examples that could be dynamic instead of static?
  • Are you explaining Claude's own capabilities back to it (it already knows)?

Dynamic system prompts are underused and underrated. Route different request types to purpose-built, minimal system prompts rather than one bloated universal one:

class SystemPromptRouter
{
    public function getPrompt(string $taskType): string
    {
        return match($taskType) {
            'summarize'  => "Summarize the following text concisely.",
            'classify'   => "Classify the input into one of the provided categories.",
            'extract'    => "Extract the requested fields from the input as JSON.",
            default      => "You are a helpful assistant."
        };
    }
}
Enter fullscreen mode Exit fullscreen mode

A targeted 20-token system prompt does the same job as a 2,000-token one if the task is specific enough.

You're Probably Not Using Prompt Caching

As of mid-2026, Claude's prompt caching feature is one of the highest-impact optimizations available and still wildly underused. With prompt caching, you can mark large, stable portions of your prompt (like a lengthy system prompt, a reference document, or a large code file) to be cached by Anthropic's infrastructure. Cached tokens are charged at roughly 10% of the normal input token price.

If you're building a code assistant that always sends a 10,000-token codebase context, prompt caching alone can cut your input costs by 90% on cache hits. The implementation is a single API change:

$response = $client->messages()->create([
    'model' => 'claude-opus-4-5',
    'max_tokens' => 2048,
    'system' => [
        [
            'type' => 'text',
            'text' => $largeSystemPrompt,
            'cache_control' => ['type' => 'ephemeral'] // Mark for caching
        ]
    ],
    'messages' => $conversationHistory
]);
Enter fullscreen mode Exit fullscreen mode

If you're not using this today, you're leaving money on the table every single hour your app is running.

Why Your Codebase Is Secretly Burning Claude Tokens Through Redundant API Calls

Beyond what's inside your prompts, many codebases waste tokens by making calls they simply shouldn't be making at all.

The Missing Cache Layer Problem

Response caching is table-stakes infrastructure for any serious Claude integration, yet it's absent from most codebases beyond prototype stage. Identical or near-identical queries hitting Claude repeatedly is pure waste. Full stop.

class CachedClaudeService
{
    public function __construct(
        private ClaudeClient $claude,
        private Cache $cache
    ) {}

    public function ask(string $prompt, int $ttl = 3600): string
    {
        $cacheKey = 'claude:' . hash('xxh3', $prompt);

        return $this->cache->remember($cacheKey, $ttl, function () use ($prompt) {
            $response = $this->claude->messages()->create([
                'model'      => 'claude-haiku-4-5',
                'max_tokens' => 512,
                'messages'   => [['role' => 'user', 'content' => $prompt]]
            ]);

            return $response->content[0]->text;
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

For many product use cases — FAQ answering, classification tasks, template-based generation — cache hit rates of 40–70% are achievable. At scale, that's a massive reduction in both cost and latency.

Model Selection Mismatch

Using claude-opus-4-5 for every task is like hiring a senior architect to check whether your HTML has a closing tag. Claude Haiku is dramatically cheaper per token and handles the majority of classification, extraction, formatting, and simple Q&A tasks with equivalent quality.

Implement a task router that matches complexity to model:

Task Type Recommended Model Why
Classification / Extraction claude-haiku-4-5 Fast, cheap, sufficient
Summarization claude-sonnet-4-5 Balanced quality/cost
Complex reasoning / Code gen claude-opus-4-5 Worth the premium

The cost difference between Haiku and Opus is roughly 25x. Routing even 60% of your traffic to Haiku will transform your unit economics. Why are you paying Opus prices for work that doesn't need it?

Monitoring: You Can't Fix What You Can't See

None of the above optimizations stick unless you build token usage observability into your application. Log input tokens, output tokens, model used, cache hit/miss status, and the feature or endpoint that triggered each call.

Tools like LangSmith, Helicone, and Braintrust provide this out of the box with minimal instrumentation. Even a simple database log table beats flying blind:

ClaudeUsageLog::create([
    'model'          => $response->model,
    'input_tokens'   => $response->usage->input_tokens,
    'output_tokens'  => $response->usage->output_tokens,
    'cache_hit'      => $response->usage->cache_read_input_tokens > 0,
    'endpoint'       => request()->route()->getName(),
    'cost_usd'       => $this->calculateCost($response->usage),
]);
Enter fullscreen mode Exit fullscreen mode

Once you have this data, patterns emerge fast. You'll find one endpoint responsible for 40% of your spend, or discover a background job that's been sending a 15,000-token context that nobody reviewed since the initial implementation.

The Real Reason Why Your Codebase Is Secretly Burning Claude Tokens

The root cause isn't technical — it's the absence of a cost-aware development culture. Token cost is invisible during local development. Nobody sees the bill when they push a feature. CI/CD pipelines don't fail because a prompt got bloated by 2,000 tokens. The waste accumulates silently until the invoice lands.

The fix is to treat token efficiency the way you treat database query performance: profile it, review it in code review, set budgets per feature, and alert when usage spikes. Build your integration with prompt caching from day one, implement a semantic retrieval layer before context gets large, and match model tier to task complexity systematically rather than ad hoc.

Every dollar you claw back from token waste is a dollar you can put toward shipping more features, handling more traffic, or simply running a more sustainable AI-powered product. Start with observability, kill the obvious bloat, then work through the list — the improvements compound faster than you'd expect.


This article was originally published on qcode.in

Top comments (0)