When an AI agent gives a bad answer on a real codebase, the instinct is to rewrite the prompt. Add a role, add "think step by step", add capital letters.

Most of the time the prompt was fine. The model read the wrong things, too many things, or nothing at all, and then did its best with what it had.

That's the shift worth making: stop polishing how you ask, and start engineering what it reads.

Two channels, kept apart on purpose

Everything a model knows during a task arrives through one of two channels. I keep them separate, and I treat them very differently.

The common mistake is to pour everything into the first one: the architecture doc, the API reference, last quarter's decisions, all pasted into the always-on instructions "so the model has context". It does. It has so much that the one rule you needed is buried on page four, and it pays for all of it on every turn.

Before

Always on

  • Loaded into every single turn
  • Small: who it's for, the rules, where things live
  • Changes rarely, reviewed like code
  • Anything in it is sent on every turn, related or not

After

On demand

  • Opened only when the task needs it
  • The spec, the screen, the API contract, the test
  • Visible as a step you can check
  • Scoped to the question that caused it

In Claude Code the first channel is the instruction file at the root of the repo, loaded every session. The second is the agent opening files itself as the work needs them.

My own setup since March is a small example. The rules file my agent loads every session is about sixty lines (who it's for, a folder map, the rules), and when the task is writing, one line tells it to open my voice profile first. Everything else stays on disk until a task asks for it.

Give it the slice, not the repo

On a mobile feature, the right context is almost never "the whole app". It's a thin slice that cuts through exactly the layers the change touches. Say the task is offline support for an orders screen on Android:

  • The spec: what the user sees, including the empty, loading, error and offline states
  • The screen and its ViewModel: where state gets shaped
  • The repository and the API contract: where the data comes from, and what it promises
  • One existing test from the same module, so the new ones match the house style
  • The conventions file: naming, dependency injection, how errors are modelled

A handful of files beats the whole tree. The model doesn't wander, it doesn't copy a pattern from a deprecated module, and when it's wrong you can see exactly which input misled it.

In practice I either name the files in the task, or I ask the agent to list what it plans to read before it starts. That list is the cheapest review there is. If it's about to study the old networking module, I stop it before it learns the wrong pattern and repeats it in ten places.

Setting up context for a new repo
  1. 1Short rules file
  2. 2A map of where things live
  3. 3Point to docs, don't paste
  4. 4A file behind every claim

Make it cite

The rule I care about most: for any claim that matters, the agent names the file it came from. "Orders are cached for a day" is an opinion. "Orders are cached for a day, per OrdersRepository.kt" is something I can open and check in ten seconds.

What a grounded answer sounds like
  • MeWhy does the orders screen flash empty on a cold start?
  • AgentThe ViewModel starts with an empty list before the first database read returns (OrdersViewModel.kt). There's no loading state in between, so the empty state renders first. I'd model Loading explicitly instead of defaulting to an empty list.
  • MeAgreed. Show me the ViewModel change only, with a test.

Citations do two jobs. They let you verify fast, and they change the model's behaviour: an agent that has to name a file tends to open one first. When it can't name one, that's your signal the answer came from training data, not from your code.

Tell it what to do when the answer isn't there

The last piece is the one most setups skip. Good context also tells the model what to do when the context doesn't hold the answer.

My weekly review command asks for a scorecard, and it ends with one plain instruction: fill in what you know, leave a dash for anything you don't have yet. That single line does more against made-up numbers than any amount of "be accurate". A model told to fill every cell will fill every cell; a model allowed to leave a gap will leave one, and the gap is the honest answer.

A prompt is how you ask. Context is what the model knows when it answers. Only one of them can make the answer right.

What I'd tell any team writing their first agent instructions

Prompt engineering asks the model to be smarter. Context engineering gives it less to be wrong about.

Weekly-ishEngineering OS, in your inboxJoin Engineering OS