The first time I tried to fix a bad agent, I made it worse.
The output was sloppy: wrong patterns, ignored conventions, a function that reimplemented something we already had. So I did the obvious thing and gave it more: more files in context, more of the codebase, the style guide, three example modules, and a long preamble about our architecture.
I reran it. The output got worse, not differently wrong: worse. It started blending patterns from files that had nothing to do with the task, citing conventions from a deprecated module I'd included "just in case."
It took me an embarrassingly long time to understand what had happened. I'd assumed the agent was underperforming because it didn't know enough. It was actually underperforming because it knew too much and couldn't tell which of the things it knew mattered for the job in front of it.
Why "give it more context" is the wrong instinct
The reflex is understandable: when a human does bad work because they're missing information, you give them the information. So when an agent does bad work, you reach for the same fix: more context, more documentation, more examples.
But an agent isn't a junior who'll read your architecture doc and form a mental model of the system. It doesn't reason conceptually about your project and then apply that understanding. It implements the task in front of it, weighing everything in its context window as potentially relevant to that task. When you load it with material that isn't relevant to this specific job, you're not giving it a richer understanding. You're adding noise, it has to work to ignore, and it often doesn't ignore it. It pattern-matches against the nearest thing in context, relevant or not.
There's a real mechanism under this. A context window is finite, and attention across it isn't free or uniform. The more data (and therefore variables) you add to the context, the more complex the solution becomes. This does not improve quality.
Once I saw this, the deprecated-module incident made sense. I'd handed the agent a pile of material and trusted it to sort the wheat from the chaff. Sorting the wheat from the chaff was supposed to be my job.
What context engineering actually is
That job has a name now: people are calling it context engineering, and it's worth separating from prompt engineering, which it often gets confused with.
Prompt engineering is about phrasing: how you word the instruction so the model understands what you want. It operates on a single request. Context engineering operates one level up: it's the practice of controlling what the agent knows at a given moment, deciding which information should be present for a particular task, getting it into a form the agent can reach, and keeping everything else out of the way.
Prompt engineering asks, "How do I describe this task well?" Context engineering asks, "What is the minimal set of true things the agent needs in front of it to do this task, and how do I make sure it has exactly that and not more?"
Most of the discipline is unglamorous. There are no clever incantations; it's closer to information architecture than to wordsmithing: organizing project knowledge so the right fragment can be assembled for the right job. The skill relies in deciding what to leave out, which is harder, because leaving things out feels like withholding help. It isn't. It's the help.
Different tasks need different contexts
The thing that finally made this click for me was noticing that the same project needs completely different context depending on what I'm asking the agent to do:
- When I'm writing code, the agent needs the coding conventions, the local constraints of the module it's touching, the task definition, and the immediate interfaces it has to respect. It does not need the product rationale for why the feature exists, and it definitely doesn't need three unrelated modules for "context." Loading those in is how you get the deprecated-pattern problem.
- When I'm doing feature or product work (thinking through what to build, not how), the agent needs a different set entirely: the product context, the user problem, the conceptual model of the domain, the constraints that come from the business rather than the codebase. Hand it the linter config and the function signatures, and you've given it nothing useful for that conversation.
- When I'm verifying (checking whether something is correct, safe, or complete), the context is different again: the acceptance criteria, the failure modes that matter, and the standard the work has to meet. This is the context people most often forget exists as its own category, because they fold verification into the build step and wonder why the agent both writes the code and declares it good.
Three tasks, three different sets of context, one project. None of the three fits comfortably alongside the others in a single window, and more to the point, none of them should sit alongside the others, because each one is noise relative to the other two. The job is to assemble the right fragment for the task at hand and swap it out when the task changes.
Organizing context so the agent retrieves only what it needs
Once you accept that, the practical work becomes a question of organization. A few principles that have held up for me:
- Separate context by task type, not by topic. The instinct is to organize knowledge the way a wiki does, by subject. For agents, organize it by when it's needed: code-task, product-task, and verification contexts. The same fact might live in more than one, and that's fine. What you're optimizing is retrieval-for-a-job, not storage.
- Keep the always-loaded layer small and stable. There's a thin set of things the agent should know in every interaction on a project: what the project is, who it's for, and the handful of constraints that are always in force. Everything beyond that should be pulled in per task, not resident by default. If your always-on context is long, it's probably carrying things that only matter sometimes.
- Write context so it can be retrieved in pieces. A single giant document forces an all-or-nothing load. Structured, sectioned context lets you (or a retrieval layer) pull the relevant fragment and leave the rest. The difference between "here is everything about the project" and "here is the part of the project relevant to authentication work" is the difference between noise and signal.
- Treat irrelevant context as actively harmful, not neutral. This is the mindset shift that matters most. Extra context isn't free insurance. Every irrelevant thing in the window is a thing the agent might anchor on. When you're deciding whether to include something "just in case," the default answer should be no, because the cost of a wrong inclusion is higher than the cost of a missing inclusion you can add back when you discover you need it.
Where structured context files fit: one pillar, not the whole thing
A structured context file (a sectioned source of truth that the agent reads) is one of the cleanest pillars of context engineering: it holds the stable, always-loaded layer, and writing it forces you to decide what actually matters.
But it's a pillar, not the building. The knowledge has to be organized so the agent can pull the right section for the task at hand, not read the whole thing every time. Make it navigable, then watch how the agent moves through it: if you're pointing it at the right file every other request, the structure isn't working. Set it up well, and the agent drifts less, even on a large project.
The deprecated module that wrecked my output a year ago was a curation failure; I'd confused giving the agent access with giving it focus, and those turn out to be close to opposite things.
We build this into Enji's PM Agent. It pulls the fragment relevant to a question instead of dumping a project's whole history into the window. The answer is bounded less by how much the agent can tell than by whether it's seeing the right thing. So when your next output disappoints, resist the reflex to add more; look first at whether the problem is too little context or too much that it can't sort.
Cut about in half from where we started. Each paragraph now carries one beat: what the file is, why structure and navigation matter, the curation reframe, the Enji point, and the closing instruction. If you need it shorter still, the third paragraph (the deprecated-module callback) is the most cuttable; it's a nice touch, but the section holds without it.
