Claude Code can burn through tokens surprisingly fast.
You give it a tiny prompt like “fix the navbar” and it responds by inspecting half the repository, reading a small library worth of files, calling tools, running tests, retrying something, and eventually making a three‑line change.
The prompt was small. The context was not.
That distinction completely changed how I use Claude Code.
I stopped thinking about token usage as a prompt‑writing problem and started treating context as a resource. The less irrelevant information Claude has to carry around, the more of its budget goes toward the thing I actually asked it to do. Here’s the setup I use.
Context over prompts
A Claude Code session contains much more than what you type. There are project instructions, files Claude reads, tool results, previous messages, skills, memory, MCP tools, and command output. All of that can end up in the context Claude has to reason over.
So this:
Fix the navbar.can quietly turn into:
Read package.json
Read CLAUDE.md
Find the navbar
Read the layout
Read globals.css
Read several components
Search for navbar references
Edit the component
Run tests
Fix the test
Run tests againThe expensive part was never the sentence. It was everything Claude decided it needed to know before answering it.
My main rule is simple:
Give Claude enough context to solve the problem, but not enough context to distract it.
If you want a more formal walkthrough of how Claude Code thinks about context, the official docs at code.claude.com and guides like the “Claude Code 101” video on YouTube are worth a skim. I took the parts I liked and then ignored the rest, like a normal developer.
Keep CLAUDE.md boring
CLAUDE.md is one of the most useful files in a Claude Code project. It is also one of the easiest places to create a context dumpster fire.
I only put things there that Claude should know almost every time it works on the project. For example:
# Project
## Stack
- Next.js
- TypeScript
- Tailwind CSS
- PostgreSQL
- Drizzle
## Rules
- Use server components by default.
- Reuse existing components before creating new ones.
- Never use `any`.
- Do not modify `.env` files.
- Run `pnpm lint` after meaningful changes.
## Structure
- `app/` - routes
- `components/` - UI
- `lib/` - shared utilities
- `db/` - databaseI do not put the entire architecture document in there.
I do not put every API endpoint in there.
I definitely do not paste a 500‑line design system in there “just in case”.
If Claude only needs something for one particular type of work, it belongs somewhere else.
My rule of thumb:
- If Claude needs to know it every time, it goes in
CLAUDE.md. - If Claude needs to know it sometimes, it becomes a skill.
- If I’m tempted to paste a whole Confluence wiki, I go drink water and reconsider my life choices.
Skills instead of repeated lectures
Skills are reusable workflows. Anthropic explains them nicely in the skills docs, but the short version is: stop repeating yourself and let the skill do it.
Suppose I have a specific process for reviewing UI. Instead of typing this every time:
Use the existing components.
Keep spacing consistent.
Do not introduce unnecessary animations.
Check mobile layouts.
Check accessibility.
Follow the existing typography.
Do not redesign unrelated parts.I turn it into a skill:
.claude/
└── skills/
└── ui-review/
└── SKILL.mdThen I can just say:
Use the UI review skill on this page.The workflow becomes reusable instead of being copy‑pasted through conversation.
A reasonable skill library might look like:
.claude/
└── skills/
├── ui-review/
├── debugging/
├── code-review/
├── seo/
├── database/
└── deploy/I would rather have six small skills I actually use than one giant “my entire engineering philosophy” skill that scares both me and the model.
Here’s plenty for a UI review skill:
***
name: ui-review
description: Review a UI implementation for consistency, spacing, responsiveness and accessibility.
***
# UI review
1. Inspect the existing design system.
2. Check reusable components first.
3. Review spacing and typography.
4. Check responsive behavior.
5. Check accessibility.
6. Look for unnecessary visual complexity.
7. Return actionable findings.
Do not rewrite the application unless explicitly asked.That’s it. The point is not to document the whole universe. The point is to give Claude a reliable workflow for one particular job.
Shameless skill plug
I also steal skills from the internet instead of reinventing everything.
One place I use is Qala, which has community skills for Claude Code and other agents. Yes, this is a slightly shameful injection of my own thing. Yes, I am still going to leave it here.
Make expensive skills manual
Some skills are useful but should only run when you really mean it. Deployment is the classic example.
I might define it like this:
***
name: deploy
description: Deploy the application to production.
disable-model-invocation: true
***Now Claude will not spontaneously decide that deployment is relevant to “fix the navbar”.
I explicitly invoke it when I want it:
/deployThis is useful for skills that:
- perform side effects
- contain large instructions
- are rarely needed
- should only run when explicitly requested
It keeps expensive workflows out of the normal path and prevents “I just wanted a button, why are we in production?” moments.
Search before you read
One of my biggest token‑saving habits is making Claude find the relevant files before reading them.
Prompts like:
Read everything in components and understand the application.are how you accidentally pay for a complete codebase tour.
Instead I’ll ask:
Find where authentication is implemented.
Only inspect files relevant to the authentication flow.
Do not modify anything.Claude can search first and then inspect the useful files. That difference matters in a large repository.
If a project has 300 components and I need one button, I don’t want Claude reading 300 components. I want it to find the button.
The same principle applies when I’m driving the tools manually. Search narrows the problem, reading expands it:
Find all references to `useAuth`.then:
Read the files relevant to that flow.The goal is not to stop Claude from exploring. It’s to make exploration deliberate instead of “open everything and hope for the best”.
Keep sessions clean: /context, /clear, /compact
When a session feels weirdly expensive, I check:
/contextIt shows what’s actually occupying the context: a giant CLAUDE.md, a huge tool response, some massive file Claude read earlier, or just a conversation that has been going on for ten different tasks.
I treat /context as a diagnostic command. If the context is bloated, I fix that before throwing another prompt into the session.
I also stopped using one Claude session for everything. My old pattern looked like:
Build the landing page.
Fix the navbar.
Add authentication.
Debug the database.
Redesign the dashboard.By the end, Claude had a huge history covering several unrelated problems and I had no idea what it still remembered.
Now I use:
/clearwhen I move to a genuinely different task. A fresh session is often better than dragging three old problems into a new one.
/clear is for starting over.
/compact is for continuing the same task without carrying the entire conversation forward.
For example:
/compact Preserve the current implementation,
remaining bugs, test results and files being modified.After a long debugging session, I don’t need Claude to remember every failed attempt. I need it to remember the current state. That distinction saves a lot of context and a lot of “wait, why are we talking about that old stack trace again?” moments.
Subagents for the big stuff
Subagents are one of my favorite ways to keep the main session clean.
Imagine asking:
Why is authentication broken?Claude might investigate:
middleware.ts
auth.ts
layout.tsx
login.tsx
register.tsx
database schema
API routes
configurationThat investigation can become a huge amount of context.
Instead I delegate the research:
Investigate the authentication bug.
Do not modify anything.
Return:
1. Root cause
2. Relevant files
3. Recommended fixThe investigation happens in a separate context and the main conversation gets the useful conclusion instead of every step in the detective novel. This is exactly where subagents make sense.
Important detail: subagents are not free. If I spawn five agents to investigate a three‑file bug, I’ve probably made the problem more expensive and slightly ridiculous.
A subagent is useful when the work is large enough to justify its own context.
Good:
Investigate this large repository and find where the billing system is implemented.Less good:
Spawn five agents and investigate this typo.I use subagents to isolate expensive work, not to cosplay as a distributed systems course.
And I restrict what subagents can do. A research agent does not need permission to edit the repository:
***
name: researcher
description: Investigate the repository without modifying files.
tools: Read, Grep, Glob
model: sonnet
***A researcher researches.
A reviewer reviews.
A deployer deploys.
The more specialized the agent, the less unnecessary work it tends to do.
MCP, tools, and hooks
MCP (Model Context Protocol) is extremely useful when I need external tools or data. It’s basically an open standard for connecting agents to tools.
But I don’t install an MCP server for everything.
If I need GitHub information once, I can just use:
gh pr listIf I need a quick Git operation:
git statusIf I need to inspect a Docker container:
docker psI already have a shell. I do not need an MCP server just because one exists and someone wrote a blog post about it.
MCP becomes more valuable when I repeatedly need a service or when the service does something awkward to access through the CLI.
The server itself isn’t always the problem; the output can be. Asking a logging service:
Give me all logs from today.and getting thousands of lines back is a lot of context for Claude to process.
Instead:
Find authentication errors from the last hour.
Return only the relevant errors and timestamps.Tools should return answers, not entire databases.
There’s a simple dividing line in my workflow:
- If a task is deterministic, I prefer a tool or script.
- If a task requires judgment, I prefer Claude.
Examples for tools:
format files
rename files
run tests
run lint
check git status
generate a known fileExamples for Claude:
decide how to refactor this
find the root cause
design the architecture
review this implementationI don’t want to spend model tokens doing something a one‑line shell command can do perfectly.
Hooks live in the same world. If I always want formatting after editing a file, I don’t want Claude to remember:
Remember to run prettier.I automate it.
Hooks are useful for deterministic actions that should happen at specific Claude Code lifecycle events. The principle is simple:
If something should happen every time, automate it instead of asking Claude to remember it every time.
It saves tokens and makes the workflow less fragile.
Models, rules, and memory
Not every task deserves maximum reasoning.
For “rename this variable”, I don’t need the biggest model on the menu.
For “why does this distributed caching system return stale data under concurrent writes?”, I probably do.
I roughly divide work into:
Simple
Fix this typo.
Rename this variable.
Explain this function.
Update this string.Medium
Implement this API endpoint.
Refactor this component.
Fix this bug.
Add this feature.Hard
Design this architecture.
Debug a complicated distributed issue.
Refactor an entire subsystem.
Investigate an unfamiliar codebase.The expensive reasoning should go toward the problems that actually need it.
The same applies to rules. If I keep telling Claude:
Do not use `any`.and then tomorrow:
Please stop using `any`.that guideline has earned a permanent home:
## TypeScript
- Never use `any`.
- Prefer explicit types.
- Reuse existing types before creating new ones.Anything I repeatedly correct usually belongs in CLAUDE.md, rules, or a skill:
Use existing components.
Don't touch environment files.
Run this test after API changes.
Don't add dependencies without asking.On the knowledge side, one of the biggest sources of wasted context is rediscovery. Every time Claude has to figure out:
Where is the API?
How does authentication work?
What command runs tests?
Where are database migrations?
What conventions does this project use?
How is deployment handled?it spends tokens learning something that could have been made explicit.
Good project configuration turns this:
discover
→ understand
→ ask
→ get corrected
→ retryinto this:
know
→ executeI keep path‑specific rules close to the code that needs them:
.claude/
└── rules/
├── frontend.md
├── backend.md
└── database.mdFrontend rules don’t need to load when I’m working on migrations. Database conventions don’t need to follow every UI request. It’s just another form of lazy loading:
Load what is relevant.And I use memory for the things Claude keeps rediscovering:
The test command is pnpm test:unit.
The payment webhook lives in app/api/webhooks/stripe.
This repository requires Node 22.
The previous caching bug came from stale Redis keys.I want memory to store recurring project facts, not become another giant documentation file.
My setup and checklist
If I were starting a project from scratch, I’d keep the structure simple:
project/
├── CLAUDE.md
│
└── .claude/
├── rules/
│ ├── frontend.md
│ └── backend.md
│
├── skills/
│ ├── ui/
│ │ └── SKILL.md
│ ├── debugging/
│ │ └── SKILL.md
│ ├── review/
│ │ └── SKILL.md
│ └── deploy/
│ └── SKILL.md
│
└── agents/
├── researcher.md
└── reviewer.mdThe mental model looks like this:
CLAUDE.md
↓
always-needed project knowledge
Rules
↓
context-specific knowledge
Skills
↓
repeatable workflows
Subagents
↓
large isolated investigations
MCP
↓
external tools and data
Hooks
↓
deterministic automation
CLI
↓
mechanical workEach piece has a job. Nothing needs to do everything.
Before I start a large Claude Code session, I run through a quick checklist:
- Is my
CLAUDE.mdactually necessary, or is it secretly a novel? - Am I asking Claude to read more files than it needs?
- Can it search before reading?
- Is this workflow better as a skill?
- Is this investigation big enough for a subagent?
- Do I really need this MCP server, or is
git statusenough? - Can a CLI command do this instead?
- Can a hook automate this?
- Should I use a cheaper model?
- Is this session carrying unrelated history?
- Should I run
/compact? - Should I just start a fresh session with
/clear?
Most of the time, at least one answer is “yes”, and fixing that saves a lot more tokens than arguing with the temperature parameter.
The mindset shift
The biggest improvement didn’t come from learning a secret Claude Code command. It came from changing the question.
I stopped asking:
How do I make Claude work harder?
And started asking:
How do I make Claude do less unnecessary work?
Give it less irrelevant context.
Give it better instructions.
Give it specialized skills.
Give expensive investigations their own context.
Use tools for deterministic work.
Use stronger reasoning only when the problem deserves it.
Keep sessions focused.
The goal is not to make Claude think less.
It is to make every token it spends actually matter.
