Agentic Development Part 4: Context Engineering for Agents
Part 2 already touched on memory as one of the four core agent components, and Part 3 discussed how to design individual tasks. This article brings both together from a more practical angle: how exactly to provide the right context to an agent, day after day, session after session. This isn’t about writing smarter prompts for a single request — it’s about building an information structure used repeatedly, by every working session, by every team member, and by every agent that touches the same project. This discipline is called context engineering, and among all the practices discussed in the Agentic Development series, it’s the one that most often determines whether an agent works with correct assumptions about the project, or guesses from scratch every time a new session starts.
Context Engineering vs Prompt Engineering
These two terms are often used interchangeably, but they focus on different things. Prompt engineering deals with how to structure instructions for one specific request — which words to use, what examples to include, what response format to ask for. This matters, but it’s single-use in nature: a good prompt for one request doesn’t automatically serve the next one.
Context engineering deals with something more persistent: what’s continuously available to the agent throughout a working session, even across the life of a project. If a prompt is what gets typed into a chat box for one moment, context is the whole body of information the agent carries while processing that moment — including project conventions, codebase structure, previous architectural decisions, and relevant work history.
flowchart LR
subgraph PE["Prompt Engineering"]
A[Instructions for One Request]
A1[Single-use, specific per moment]
end
subgraph CE["Context Engineering"]
B[Project Conventions]
C[Codebase Structure]
D[Previous Architecture Decisions]
E[Technical Specs from the Previous Series]
B1[Persistent, reused throughout sessions/project]
end
PE -.runs on top of.-> CEWhen someone opens a new chat session with a language model, the model knows nothing about the specific project — it doesn’t know the project uses PostgreSQL rather than SQLite, doesn’t know the agreed error-handling conventions, doesn’t know the architectural decisions made last month and why. Without prepared context, the model fills these gaps with generic assumptions that are plausible but often don’t match the project’s actual conditions. Context engineering is the practice of solving this “cold start” problem once, in a form reusable by every subsequent session — rather than re-explaining it manually every time in each new conversation.
This distinction isn’t just academic terminology. Teams that focus only on prompt engineering — trying better wording on every request — often experience inconsistent results across sessions. Teams that invest in context engineering get far more stable results, because the foundation of project understanding is already correct from the start of every session, regardless of how the specific prompt is written.
The Context Layers an Agent Needs
The context an agent needs isn’t a single block of information, but several layers with different functions and different change frequencies.
| Layer | Contents | How Often It Changes |
|---|---|---|
| Project conventions | Directory structure, code style, build/test commands, general rules | Rarely — only when team conventions change |
| Technical specs | ERDs, API contracts, acceptance criteria (from the Spec-Driven Development series) | Per feature — changes when new requirements arrive |
| Decision history | Why an approach was chosen, what alternatives were considered and rejected | Grows over time, rarely deleted |
| Actual codebase | The real contents of code files | Changes with every commit |
These four layers ideally shouldn’t be mixed into one big document. Project conventions are relatively stable and fit being loaded fully at the start of every session. Technical specs are feature-specific and only relevant while working on that feature. The actual codebase is too large to load entirely and is better accessed on demand through search, rather than read in full up front.
flowchart TD
A[Project Conventions<br/>stable, loaded fully at session start] --> E[Agent Context While Working]
B[Active Feature Technical Spec<br/>per feature, loaded when relevant] --> E
C[Decision History<br/>referenced when 'why' questions arise]--> E
D[Actual Codebase<br/>accessed just-in-time via search/grep] --> EThis separation isn’t just about tidy file organization — it’s about preventing a fast-changing layer (the actual codebase) from making another layer that should be stable (project conventions) hard to maintain because they’re mixed together.
Convention Files as Persistent Context
The project conventions layer, in practice, is almost always realized as a single markdown file placed at the repository root and automatically read at the start of every agent session. This format has already converged into a widely used standard across various coding agent tools, usually under a name like a project instruction file that the tool in use reads automatically.
Content that’s commonly effective in such a file:
# [Project Name] — Context for Agents
## Overview & Tech Stack
[Language, framework, database, deployment target — concise]
## Project Structure
[High-level directory layout, where to find what]
## Code Conventions
[Naming style, error handling patterns, test structure]
## Frequently Used Commands
[Build, test, lint — exact commands that can be run directly]
## Constraints and Prohibitions
- DO NOT [specific things that have caused problems before]
- ALWAYS [conventions that must be followed]
## Anti-Patterns That Have Happened
[Mistakes agents made before and how to avoid them — this section
grows over time based on real failures, not written speculatively
at the start]
Several important principles about this file are backed by empirical findings, not just opinion:
Write it manually, don’t auto-generate and use it as-is. Studies comparing human-written context files versus model-generated ones found results that defy intuition: auto-generated files actually decreased task success rates while adding inference cost because of their bloated size. Human-curated files give clear performance improvements. Using auto-generation as a rough draft to be manually edited is fine, but committing raw generated output without curation is a practice best avoided.
Add rules based on real failures, not speculation. The most common temptation: every time an agent makes a mistake, the first reflex is to add a new rule to the convention file. Over time the file accumulates contradictory rules and patches for specific cases that are no longer relevant. This bloated file actually lowers task success rates instead of raising them — more rules doesn’t automatically mean better results.
Stale structural references are more dangerous than no reference at all. A file documenting directory structure or architecture that has changed since the file was written actually misleads the agent — pushing it toward broader exploration than needed without improving task success.
Place the most critical rules at the top of the file, in explicit bullet form. In long sessions, there’s a phenomenon where instructions “buried” in the middle of long paragraphs tend to be ignored compared to instructions written as bullets under a clear heading like “Constraints” or “Don’t Do”.
A convention file that’s too long and full of contradictory rules isn’t a sign of a well-documented project — it’s a sign of a file that was never tidied up. Do regular reviews to remove rules that are no longer relevant, rather than only ever adding and never pruning.
The Context Window as a Limited Resource
The natural but mistaken temptation is thinking “the more context given, the better the result” — just put in all the documentation, all the decision history, all the codebase contents, just in case, so the agent never lacks information. This approach fails for two different reasons.
Reason one: capacity limits. The context window, though much larger than previous model generations, still has a limit. Information beyond that limit can’t be processed at all.
Reason two, subtler and more often overlooked: noise interferes with signal even before the capacity limit is reached. Relevant information can get “buried” under irrelevant information, making it hard for the agent to find and prioritize important details even though technically all that information is still in context. Agent architecture research notes that long chains of autonomous actions make the cost of context mistakes a first-class operational problem — the longer the action chain before human intervention, the greater the impact of noisy or irrelevant context.
flowchart TD
A[Strategy: Include All Information] --> B{Under Capacity Limit?}
B -- Yes --> C[Noise Still Interferes with Signal]
B -- No --> D[Information Truncated Without Control]
C --> E[Reasoning Quality Declines]
D --> E
F[Strategy: Selective & Just-in-Time] --> G[Only Relevant Information per Moment]
G --> H[Signal Stays Clear]The practical implication: “include everything just in case” isn’t a safe strategy — it’s a strategy that actively degrades the agent’s work quality. The more appropriate question isn’t “what information might be useful”, but “what information is relevant to the step currently being worked on”.
Retrieval Strategies: What Gets Loaded, When
There are two basic approaches for deciding what information enters the agent’s context, and both have their place depending on the type of information.
Pre-loading (loaded at the start) — fits information that’s relatively small and always relevant regardless of the specific task being worked on, like a project convention file. Because its size is controlled and its relevance universal, the cost of loading it at session start is worth the benefit of not having to search for it repeatedly.
Just-in-time retrieval (loaded when needed) — fits information that’s large or task-specific, like the codebase contents as a whole. Instead of loading the entire project at the start, the agent keeps lightweight references (file paths, saved queries) and loads the actual contents only when truly needed at a particular step — using search tools (similar to those discussed as a tool use category in Part 2) to navigate and fetch information as needed in the moment.
flowchart LR
A[Session Start] --> B[Pre-load: Project Conventions]
B --> C[Agent Starts Working]
C --> D{Need Specific Details?}
D -- Yes --> E[Just-in-Time: Search/Grep/Read Specific File]
E --> C
D -- No --> F[Continue with Existing Context]The most effective approach in practice is often hybrid — not purely one or the other. Small, always-relevant information is loaded at the start for speed; further exploration happens independently as needed, using the same navigation tools human developers use to explore unfamiliar codebases — not relying on a static index that can go stale as soon as the codebase changes.
For very large codebases, some teams add a semantic retrieval layer (search based on meaning, not just keyword matching) as a complement to direct navigation — especially useful for cross-service refactoring that requires understanding relationships between code parts that aren’t always visible from directory structure alone. But for most projects, the simple combination of pre-loading conventions and just-in-time search is effective enough without needing complex additional infrastructure.
Start with the simplest approach: a convention file loaded fully at the start, plus standard search tools for just-in-time exploration. Add more advanced retrieval layers only when there’s real evidence the simple approach is no longer adequate — not built up front because it sounds more sophisticated.
Keeping Context Fresh: Continuous Updates
The stale spec problem was already covered in Part 4 of the Spec-Driven Development series — specs that were accurate when written but never updated as implementation proceeded, becoming a source of confusion in later sessions. The exact same problem applies to context files: documented conventions can quickly drift from reality as the project keeps evolving.
The difference from specs is that context files usually live longer and get used more often — so the cost of letting them go stale is far greater. A stale spec for one feature only affects that feature; a stale convention file affects every subsequent working session, without exception.
Effective practices to prevent this:
- Update as part of the definition of done — just like the practice in Part 4 of the Spec-Driven Development series, when a task changes a documented convention, updating the context file is part of “task complete”, not a separate administrative step that’s easy to postpone
- Version-controlled like code — the convention file is treated as part of the same repository, reviewed through the same process as code changes, not freely edited without a trace
- Scheduled periodic reviews — besides reactive updates when conventions change, routine reviews (say quarterly) to remove outdated guidance help prevent the accumulation of contradictory rules over time
A stale context file is more dangerous than having no context file at all. Without a file, the agent will at least explore the codebase directly to understand actual conditions. With a stale file, the agent trusts wrong information as truth, and this error can propagate into every subsequent decision undetected until much later.
Context for Long Sessions and Multi-Session Work
Complex tasks often require many iterations of the plan-act-observe loop (covered in Parts 1 and 2), and this creates its own challenge: conversation history and tool call results keep growing and eventually approach the context window limit, even though the session itself isn’t finished.
The strategy used to handle this is called compaction — condensing long history into a denser version, retaining still-relevant information while discarding what’s no longer needed. What makes compaction hard to do well isn’t the summarizing technique, but the decision of what to keep and what to discard — architectural decisions and unfinished implementation details need to be kept, while single-use tool call results that are no longer relevant can be safely discarded.
flowchart TD
A[Long Session Running] --> B{Context Approaching Limit?}
B -- Not yet --> A
B -- Yes --> C[Compaction: Condense History]
C --> D[Keep: Architecture Decisions,<br/>Unfinished Bugs, Implementation Details]
C --> E[Discard: Single-Use Tool Call Results<br/>No Longer Relevant]
D --> F[Continue Session with Compressed Context]
E --> FA complementary, simpler, and often more reliable approach is externalizing state — storing work progress in external files instead of relying only on memory inside the context window. A simple pattern like a continuously updated work progress note (similar to a to-do list the agent writes and re-reads) lets the agent track progress and inter-step dependencies across many tool calls, without losing the thread even if the context window needs compressing or the session even needs restarting from scratch.
For work that truly spans many separate sessions — not just one long session — an effective pattern is closing each session by writing an explicit summary to a file (what’s done, what’s still pending, what decisions were made and why), so the next session doesn’t have to re-guess from scratch the last state the work was left in.
A sign a session has gotten too “dirty” to continue well: a mix of several unrelated tasks in the same session, or repeated corrections of the same mistake without improvement. At this point, starting a new session with clean context — aided by a written summary from the previous session — is often more productive than forcing a session full of confusing history to keep going.
Anti-Patterns in Context Engineering
Several patterns often appear and weaken context effectiveness even though they look like good effort on the surface:
Context overload — including everything “just in case”. Already discussed above: more information doesn’t automatically mean better results, and excessive noise actually interferes with important signals, even before the context window is truly full.
Stale context. Convention files or structural references never updated as the project changes, misleading the agent with information that looks authoritative but is no longer accurate.
No structure, so the agent has to “guess” every session. Without a clear context file, every new session starts from zero — the agent has to re-derive project conventions from reading existing code, with results varying depending on which file happens to get explored first.
Over-reliance on one big file without hierarchy. Mixing stable conventions, feature-specific technical specs, and highly granular implementation details into a single file that keeps bloating. The layer separation discussed at the start of this article — conventions separate from feature specs, separate from the actual codebase — prevents one file from becoming too large and too hard to maintain accurately.
Adding rules reactively without ever removing. The pattern already touched on in the convention file section — every failure is responded to by adding one new rule, without ever reviewing whether old rules are still relevant or now contradict newer ones.
The clearest sign of immature context engineering isn’t “the agent makes mistakes” — that will always happen occasionally. The sign is the same mistake repeating across different sessions even though it was “fixed” before, which usually means the fix was only given as a momentary correction in the conversation, not actually entered into the persistent context that later sessions read.
Summary
- Context engineering differs from prompt engineering: a prompt is a single-use instruction for one moment, context is the persistent information an agent carries through a session and across sessions
- The context an agent needs is layered — project conventions (stable, loaded fully), technical specs (per feature, from the Spec-Driven Development series), decision history, and the actual codebase (accessed just-in-time) — and ideally shouldn’t be mixed into one big document
- A project convention file is most effective when written and curated by humans, not auto-generated and used as-is — empirical studies show auto-generated files actually lower task success rates
- Add rules to convention files based on observed real failures, not speculation — a bloated file with contradictory rules lowers performance, not raises it
- “Include all information just in case” is a mistaken strategy — noise interferes with signal even before the context window fills, so selective retrieval beats loading everything
- Combine pre-loading (for small, always-relevant information) with just-in-time retrieval (for large or task-specific information) — this hybrid approach is more effective than purely either one
- A stale context file is more dangerous than no file at all — updating context is part of the definition of done, just like updating specs in the previous series
- For long sessions, use compaction (condensing history while retaining important decisions) and externalizing state (storing progress in external files) so work doesn’t lose the thread even if context gets compressed or the session restarts