There is a temptation, when a coding agent disappoints, to blame the model. The output was confident and plausible and wrong; the obvious conclusion is that the model is not clever enough yet. In my experience that conclusion is almost always premature. Far more often the model was reasoning faithfully about a situation it had been told almost nothing about, and produced exactly the kind of answer you would expect from a capable colleague who had been dropped into your codebase, handed no documentation, and asked to ship.
This is the second article in a series on engineering with Claude Code. The first established the shift from autocomplete to a true agentic collaborator. This one makes a single argument: the quality of an agent's output is bounded by the quality of the context it works from. The model matters, but the context sets the limit. Once you accept that, the most important skill stops being clever prompt wording and becomes something more durable: context engineering, the discipline of deciding what the agent should know, when, and in what form.
It is also the least glamorous skill on offer, which is probably why it pays off so well. Anyone can paste a clever instruction. Almost nobody maintains the context their agent depends on.
Why context, not cleverness, sets the ceiling
Consider two extremes. A state-of-the-art model with poor context will confidently invent a configuration pattern your project abandoned a year ago, call a helper that no longer exists, and arrange files in a layout your team would reject on sight. It is not malfunctioning. It is filling gaps with the statistically most plausible guess, and a plausible guess about software in general is frequently the wrong guess about your software in particular.
Now invert it. A more modest model, given a crisp description of your architecture, the exact commands to build and test, the conventions you actually follow, and a pointer to the canonical example of the pattern in question, will produce work that lands close to mergeable. The difference is not intelligence. It is grounding.
A capable model with poor context produces confident, wrong work. A modest model with excellent context produces reliable work. Context is not a tuning knob on top of the model. It is the product you are actually shipping to the agent.
This reframing has a practical consequence. When the agent goes wrong, your first instinct should not be to reach for a bigger model or a more elaborate prompt. It should be to ask: what did it not know that it needed to? Nine times in ten, the fix is a sentence of missing context, not a smarter sentence of instruction.
CLAUDE.md: the project's memory
The most direct lever for grounding the agent is a project memory file, conventionally named CLAUDE.md, that Claude Code loads automatically at the start of a session. Think of it as a briefing for a competent newcomer who will read it once and then act, rather than as documentation for humans. It lives in the repository, travels with the code, and is reviewed like any other source file.
The contents that earn their place tend to be the same across projects: a short statement of what the system is and how it is structured; the canonical commands to build, test and run it; a map of the directory layout so the agent does not have to rediscover it each time; the conventions that are genuinely non-obvious; and an explicit list of things not to do. That last category is underrated. A clear "never edit the generated files in src/api/__gen__" prevents a whole class of confident mistakes.
Below is an illustrative project-level file. Most of it is pointers and prohibitions rather than prose:
# CLAUDE.md
## What this is
Billing service for the platform. Express + TypeScript API,
PostgreSQL via Prisma, deployed as a container. Talks to the
payments provider through `src/providers/` — never call the
provider SDK directly from route handlers.
## Build, test, run
- Install: package manager is pnpm (NOT npm — lockfile is pnpm)
- Test: `pnpm test` runs unit; `pnpm test:int` needs Docker up
- Run dev: `pnpm dev` (expects a local .env, see .env.example)
- Lint: CI fails on warnings; run lint before proposing a diff
## Layout
- src/routes/ thin HTTP handlers, no business logic
- src/services/ business logic lives here, fully unit-tested
- src/providers/ external integrations behind interfaces
- src/db/ Prisma schema + migrations
- src/api/__gen__/ GENERATED — do not edit by hand
## Conventions that are not obvious
- Money is always integer minor units (pence), never floats.
- All service methods take and return DTOs from `src/dto`, not
Prisma models — keep persistence types out of the service layer.
- Errors thrown to routes must extend `AppError`; the error
middleware depends on its `.status` field.
## Do not
- Do not add new dependencies without flagging it explicitly.
- Do not weaken or skip tests to make a change pass.
- Do not edit files under src/api/__gen__/.
There is also a personal, user-level file (typically at ~/.claude/CLAUDE.md) that applies across every project you work on. This is the right home for preferences rather than project facts: how you like commit messages phrased, that you prefer the agent to ask before large refactors, that you favour standard-library solutions over new dependencies. I keep personal taste in the global file and project truth in the repository file. The repository file is reviewed by your team and reflects shared reality; the global file is yours and reflects how you like to work.
What makes a CLAUDE.md good, and what makes it harmful
A good memory file is concise and high-signal. Every line should change the agent's behaviour; if a line would be equally true of any project in your language, it is probably noise. Favour pointers over dumps. "The canonical example of our repository pattern is src/services/invoice.service.ts" is worth more than three pages pasted inline, because it sends the agent to the live, current truth rather than a snapshot that will rot. Capture the things that are not discoverable from the code: the historical decision, the sharp edge, the convention that has no syntactic marker.
The failure modes are just as consistent, and it helps to name them so you can spot them in review:
- Stale instructions. The file describes the project as it was, not as it is. This is worse than no file at all, because the agent trusts it. A memory file that lies is a liability; treat it as code and let pull requests update it.
- Huge irrelevant pastes. Dumping an entire style guide, a vendor's API reference, or a wall of historical notes feels thorough but actively degrades performance (more on that below). Link to the source; do not embed it.
- Vague aspirations. "Write clean, maintainable, well-tested code" instructs nothing. The agent already intends to. Replace aspiration with specifics: what clean means here, which test command, where the example lives.
A simple test: if you handed the file to a new senior engineer, would each line save them a question or a mistake? If not, cut it.
The hidden cost of bad context
It is tempting to believe that more context is always safer, and that if in doubt you should include it. This is wrong, and understanding why changes how you write everything you feed the agent.
An agent has a finite attention budget. Every token of context competes with every other token for the model's focus. Suppose you pad the context with material that is irrelevant to the task at hand: the entire database schema for a change that touches one CSS file, or a decade of changelog for a one-line fix. That wastes tokens and money, and it also dilutes the signal. The relevant instruction now sits among a hundred irrelevant ones, and the model's attention is spread accordingly. Relevance beats volume.
This is why the discipline is called context engineering rather than context collection. The skill is curation: getting the right information in front of the agent at the right moment, and keeping everything else out. A focused thousand-token briefing will outperform a sprawling ten-thousand-token one on the same task more often than newcomers expect. When a session starts going sideways, an overstuffed context is as likely a culprit as a thin one.
Steering within a session
The memory file sets the baseline, but most grounding happens live, in the back-and-forth of a working session. Three habits do most of the work.
Point at the right files. Rather than describing how something works, name the file that demonstrates it and let the agent read the current truth. "Follow the pattern in invoice.service.ts" is both shorter and more accurate than any paraphrase you could write, and it cannot go stale.
Correct course early. Agentic work compounds. A wrong assumption in the second step becomes a wrong design by the tenth, and unwinding ten steps is far more expensive than redirecting at step two. Read what the agent proposes as it proposes it, and interrupt the moment the direction drifts. Cheap correction early beats expensive rescue late.
Give an example of the target. When you want a specific shape of output, show it. The gap between a vague instruction and a precise one is large, and closing it is probably the most useful change most people can make to how they prompt:
--- Vague (the agent must guess your intent) ---
"Add validation to the user endpoint."
--- Precise (grounded in your reality) ---
"In src/routes/user.ts, add request validation to the POST
/users handler. Use the existing zod schema approach — see
createOrderSchema in src/routes/order.ts for the exact
pattern. On failure, throw a ValidationError (extends
AppError) so the error middleware returns 400. Add a unit
test mirroring the one in order.test.ts. Do not introduce a
new validation library."
The precise version is longer, but notice what it does: it names the file, points at the canonical pattern, states the error contract, specifies the test, and closes off a tempting wrong turn. It leaves almost nothing to guess. That is context engineering in miniature: every clause removes an opportunity for confident error.
Where this leads
Grounding is not only about getting better answers today; it is the foundation on which the rest of this series rests. Safe autonomy, the subject of the next article, is only safe when the agent is operating from accurate context, because an agent acting independently on a false picture of your system is precisely the thing you should fear. And automation, which we reach later in the series, is only trustworthy when the context that drives it is current and curated rather than stale and bloated. An unattended agent inherits whatever briefing you left it; if that briefing lies, the automation lies at scale.
So, to repeat the unglamorous takeaway: treat context as the artefact you are actually engineering. Maintain the memory file like source code. Curate live context for relevance, not volume. Steer early and concretely. Do that, and a great deal of what looks like model limitation goes away, because most of the time the model was never the bottleneck. The context was, and the context is the one thing entirely within your control.