A context window is a limited, expensive resource, and context engineering is the discipline of deciding what earns a place in it: the system prompt, tool definitions, retrieved files, conversation history. CLAUDE.md and project instructions, why targeted file search beats loading whole directories, and the caching reason a stable prefix goes first.
what context engineering means
A coding agent's context window is not storage. It is the entire working memory for the current turn: the system prompt, the tool definitions, whatever files got pulled in, and the conversation so far, all competing for a fixed budget of tokens and for the model's attention within that budget. Context engineering is the decision, made repeatedly through a session, of what earns a place in that budget and what does not.
The instinct when a window is technically large enough is to just put everything in it: the whole repository, the whole conversation history, every tool the agent might conceivably need. That instinct is wrong for reasons that have nothing to do with hitting the token limit. Every token you add is billed, every token slows the response, and every irrelevant token is something the model has to read past on the way to the token that actually matters. A window that is technically under budget can still be badly engineered.
instruction files: say it once, at the front
Most coding agents look for a project-level instructions file, commonly named CLAUDE.md or AGENTS.md, and load it automatically at the start of a session. It typically holds the things a new contributor would otherwise have to ask about: how to run the build, which directories are generated and should not be edited by hand, what the project's conventions are, where the tests live.
The value of putting this in a file rather than leaving the agent to figure it out is not just convenience. Discovery by trial and error costs tool calls, each of which costs tokens and time, and it costs them again on every fresh session that has to rediscover the same facts. A short, accurate instructions file amortizes that cost to zero after the first read.
Keep it short and keep it true
An instructions file that has grown to several thousand words of every possible edge case stops being memory and starts being noise the model has to skim past on every turn. It is also a maintenance liability: an instruction that no longer matches the codebase actively misleads the agent, which is worse than no instruction at all. The useful version is closer to a page than a manual, updated when it goes stale rather than expanded when it goes unread.
# CLAUDE.md
## Build
npm run build # tsc + esbuild, see scripts/build.mjs
npm test # vitest, watch mode off in CI
## Layout
app/ Next.js App Router pages, one dir per route
components/ shared UI, no route-specific logic here
lib/ plain functions, no React imports
## Rules
- Never hand-edit files under generated/
- Server components by default; mark 'use client' only when needed
- No new dependencies without checking package.json firstsearch beats loading the whole directory
The second lever is how the agent gets code into context in the first place. One approach is to load whole files or whole directories up front, on the theory that more coverage means fewer surprises. The other is to search: grep for the relevant symbol, run an embedding query, follow an import graph, and pull in only what the current step actually needs.
Search wins for a structural reason, not a subtle one. A repository is almost entirely irrelevant to any single task. Loading a directory returns all of it anyway, relevant or not, and the model has to spend attention distinguishing the two. A targeted search returns close to just the relevant part, which means more of the window is signal and less of it is the agent silently reasoning about code it will never touch.
- Cost. Tokens for a directory dump scale with directory size. Tokens for a search result scale with the size of the answer, which is usually far smaller.
- Freshness. A search re-run each turn reflects the current state of the file. A file loaded once and kept in history can go stale if another tool call edits it later in the same session.
- Cacheability. Bulk-loaded content is usually task-specific and changes every turn, so it cannot sit in a cached prefix. It has to be paid for fresh, repeatedly.
This is not an argument against ever loading a full file. Once search has identified the file that actually matters, reading it in full is often the right move, since the agent then has the surrounding logic it needs to make a correct edit. The point is sequencing: search first to find where to look, then load narrowly, rather than loading broadly on the chance that the answer is in there somewhere.
stable prefix first, because caching is priced on it
Prompt caching, as implemented by Claude and similar systems, works by keying on a prefix: when a new request's content up to some point exactly matches a previous request, that matched portion is served from cache at a steep discount instead of being reprocessed at full input price. The catch is that the match has to hold from the very start of the prompt. One character different early in the context invalidates the cache for everything after it.
That mechanical fact turns into a concrete ordering rule. Put the parts of the context that never change turn to turn first: the system prompt, the tool definitions, the project instructions file. Put the parts that change constantly last: the specific files for this step, the most recent tool results, the latest user message. Structured this way, the stable prefix is cached once and reused, cheaply, on every subsequent turn of the session, while only the genuinely new tail costs full price.
Get the ordering backwards, for instance by putting a timestamp or a freshly retrieved file near the top of the prompt, and every turn effectively becomes a cache miss even though most of the content is identical to the last request. The saving from caching is not a bonus feature that shows up regardless of how you build the prompt; it is a direct consequence of holding the prefix still.
the cost of more context is not just tokens
It is tempting to treat context as a free good up to the window limit: if the model can technically hold two hundred thousand tokens, why not use most of them. Three separate costs argue against that, and they apply well before the window is anywhere near full.
- Money. Every token in the volatile tail of the prompt, and every token after the first place the prefix diverges from the previous turn, gets billed at the full input or cache-write rate rather than the cheap cache-read rate. A context padded with marginally useful material pays that rate for material that did not help.
- Latency. A larger prompt takes longer to process before the model produces its first output token, on every single turn of a session that might run for dozens of turns. That delay compounds in a way a one-off cost does not.
- Attention. This is a mechanism, not a measured statistic: transformer attention is distributed across everything present in the context, so material that is irrelevant to the current step is not free to ignore, it is competing weight the model has to allocate away from the material that matters. As a rule of thumb, a context stuffed with tangential files makes an agent more likely to reference the wrong one, cite outdated code it should have discarded, or lose track of the actual instruction buried earlier in a long turn.
putting it together
None of the four levers above is exotic. A short, accurate instructions file. Retrieval before bulk loading. A prefix ordered from stable to volatile. A default toward less context rather than more, revised upward only when a step actually needs the extra material. What makes this a discipline rather than a checklist is that it has to be re-applied as a codebase and a session both grow, not set once at project start.
A session that has been running for an hour has a long history behind it. Left alone, that history keeps growing and every turn re-sends more of it. The practical move is to summarize settled portions of the conversation once they stop being live, keeping the summary in the semi-stable middle of the prompt rather than replaying the full back-and-forth verbatim on every turn. Done well, this looks less like a single optimization and more like ongoing maintenance of the same kind a long-lived process needs anywhere else in software.