Skip to content
Pere Villega
Go back

Context Engineering: The Skill That Replaced Prompt Engineering

9 min read

A prompt is only one part of what a coding agent sees. The rest may include CLAUDE.md files, tool definitions, MCP output, files the agent has read, and conversation history. When an agent performs poorly, rewriting the instruction may help, but missing or noisy context is often the more important problem.

The Distinction That Matters

Prompt engineering optimises the instruction. Context engineering optimises the environment. The difference is similar to writing a good email to a colleague versus maintaining a useful team reference: one helps with a request, while the other supports repeated work.

I use a four-layer framework for thinking about that environment. A prompt remains part of it, but each layer addresses a different source of failure.

The first layer is prompt craft: writing clear, specific instructions. It is necessary, but beyond simple tasks it cannot compensate for missing project knowledge or vague success criteria.

The second is context engineering: curating what the model sees beyond the prompt, how it is structured, and what is missing. This is the focus of this chapter.

The third is intent engineering: encoding goals and boundaries for agents working with more autonomy. Constraints, success criteria, and verification steps let you delegate without prescribing every keystroke. This is related to prompt clarity, but it focuses on making the desired outcome testable.

The fourth is specification engineering: making organisational knowledge usable by agents. Design documents, API schemas, domain models, and diagrams can provide more precise constraints than an informal paragraph.

It is easy to keep polishing layer 1 when the problem is in layers 2, 3, or 4. Context is a useful place to look first because it is accessible and often neglected.

What Good Context Looks Like

In my experience, context should focus on the HOW: standard operating procedures, runbooks, how to test the full app. If you use non-standard practices, also document how to instrument, how to write tests, how to feature flag, or any other custom process. The HOW provides tools and guidance without restricting output. Think of it as setting up the workbench rather than dictating what to build. The WHAT, the task itself, will use all this context to complete itself.

There’s also a layer of context that informs architectural decisions: ADRs, mission statements, domain knowledge. These help the agent make choices that align with your organisation’s approach rather than defaulting to whatever’s most common in its training data. I’ve lost count of the times an agent has defaulted to using Kubernetes just because the project has a Dockerfile and Kubernetes has a lot of tutorial coverage. Context fixes this.

The most important principle, I think, is that context should be easy to access and, the more important it is, the easier it should be. All general context should be co-located with the codebase as much as feasible. Accessed via CLAUDE.md (just keep it small), a docs folder in the repo, or reference files loaded on demand via a memory MCP.

This includes a plan file for tasks in progress, so that an agent can stop and resume without losing track of completed work. If the context window fills, the agent crashes, or the work diverges, the plan file is a recovery mechanism. It does not solve every memory problem, but it preserves task state that would otherwise exist only in the conversation.

The Invisible Context Problem

But how do you figure out what belongs in context and what doesn’t? Someone else’s pain is instructive here.

Michael Mueller at re:cinq published an analysis of OpenAI’s Harness Engineering case study, which described a team growing from 3 to 7 engineers and a codebase reaching one million lines in five months. Mueller’s key lesson sounds obvious until you internalise it: from the agent’s perspective, anything it can’t access in-context doesn’t exist.

The architectural decision agreed in a Slack thread, the domain model in someone’s head, and the convention everyone “just knows” are all invisible unless the agent can retrieve them. From the agent’s perspective, unavailable knowledge might as well not exist.

What failed for them was a massive instruction file telling the agent everything. Context is a scarce resource and a giant instruction file crowds out the actual task. Too much guidance becomes non-guidance; the same principle as when everything is important, nothing is. And it rots instantly. Sound familiar? It’s the same failure mode as those 200-page onboarding documents nobody reads. We’ve been making this mistake long before agents came along.

What worked was treating instructions as a table of contents pointing to a structured knowledge base. All the design docs, architecture decision records, API schemas, domain models. All version-controlled, all in-repo, all machine-readable. The instructions tell the agent where to look, not what to think, and the agent loads on demand.

Some context is harder to colocate, such as Slack conversations, ADRs in Confluence, or GitHub pull requests. MCP can make these sources available, but retrieval may be incomplete or happen too late. If a Slack thread determines whether the agent builds the right thing, promote the decision into a durable project document rather than relying on retrieval at the critical moment.

Reducing Context Needs

Of course, the best context is the context you don’t need. A lot of context takes too much of the limited context window. Yes, limited. Even with 1M context window, the needle in a haystack problem still exists.

There are structural ways to reduce this. None of them are particularly novel, which I suspect is part of why they’re undervalued.

The first is keeping tasks small. A well-scoped task needs less context than a vague one. “Add a retry mechanism to the payment service with exponential backoff” needs the context for that service. “Improve the payment system” needs the context for everything. A vertical slice from UI to database needs the context for that slice, not the entire codebase. Scoping isn’t just good project management, it’s context management.

The second is writing good code with low coupling. When modules are well-separated, the agent only needs to read the relevant ones. This is not a new insight; it’s the same reason humans prefer well-structured codebases. But it matters more now because the cost of poor structure is measured in token waste and degraded model performance, not just developer frustration.

The third is uniform stack choices. Consistent patterns, database, and language across the project mean less context explaining variations. Every time the agent has to understand “oh, this service uses a different ORM,” that’s context budget wasted on accidental complexity. This also means using less popular libraries or conventions has a penalty, which is an unfortunate side-effect.

Following good engineering practices (surprise!) makes AI tools work better. I suspect this will be a recurring theme in this series. And, honestly, if the main outcome of the AI agent era is that it finally forces teams to maintain good practices and clean architecture, I’ll take it.

Loading Context at the Right Moment

So you’ve structured your knowledge base, co-located the important bits, and kept tasks small. But there’s a subtlety that’s easy to miss: when context gets loaded matters almost as much as what gets loaded.

Most CLAUDE.md setups I’ve seen, including my own early attempts, take a static approach. You list everything the agent might need upfront, or you rely on the agent to decide what to read. Both have problems. Loading everything upfront spends context on material that may be irrelevant. Self-service fails when the agent proceeds with what is already available instead of looking up what the task requires.

The better pattern, I think, is using hooks. Adding some bits of classical software that trigger automatically when certain events occur in the agent’s workflow. In Claude Code, for example, you can set up a hook that fires after the user submits a request. That hook can inspect the task, identify which documents are likely relevant, and inject them into context before the agent starts working. Hooks can use Haiku to execute a task, which may be an alternative to a coded script to load context, with the downside of some LLM randomness added to the mix. In either case, the agent doesn’t decide what to load; the infrastructure decides for it, based on the task at hand.

This is a meaningful distinction. It moves context loading from “the agent remembers to look things up” to “the system ensures the right context is present.” It’s the difference between hoping a new team member reads the onboarding docs and having an automated checklist that blocks them until they do.

Which brings me to a related insight that Jesse Vincent articulated well: the difference between rules and gates. A rule such as “remember to check the API contracts” leaves room for the agent to decide it does not apply. A gate states an explicit sequence and a concrete question: before modifying an endpoint, read docs/api-contracts.md and confirm the relevant contract is in context.

That formulation makes omissions easier to notice, but it is still a prompt. It does not make the checkpoint impossible to skip. If the boundary must be guaranteed, enforce it with classical software outside the model: a hook, test, policy check, or CI job which can actually block progress.

Hooks and prompt-level gates still work well together. The gate makes the expected sequence explicit; the hook can load the required context or reject a transition. Just distinguish a useful behavioural checkpoint from a hard control.

XML Tags and Delimiters

This might seem like a formatting detail, but it’s actually fundamental to how models parse context. Bear with me.

XML tags and delimiters can provide a structural signal that helps the model distinguish between different types of content. Wrapping instructions in <instructions> tags and examples in <example> tags makes the boundary explicit instead of asking the model to infer it.

The intended impact is clearer separation between instructions, examples, and quoted content. Use delimiters when those boundaries are otherwise ambiguous, then test whether they improve the task rather than assuming a universal benefit.

Context Is the New Code

I opened this chapter by arguing that the instruction is only one part of the environment. Mueller’s framing captures why: context engineering is the discipline of maintaining structured, version-controlled knowledge that agents consume. It is good information architecture applied to a new reader.

This is an organisational capability, not just an individual practice. Teams that keep critical knowledge current, accessible, and close to the code give both humans and agents a better chance of making sound changes. Teams that leave decisions in transient chat threads should expect retrieval failures, regardless of which model they use.


Share this post on:

Previous Post
An Opinionated Starting Point for Claude Code Users
Next Post
Running AI Coding Agents on Hetzner