Context Is Everything

AI agents are only as good as the context you give them. Anyone running large projects with agents gets further with better context management than with a better model. A field report from two years of practice.

FC
· 17 min read

Two years ago, I started working seriously with coding agents, as the primary tool in my daily work and not just to try them out or for demos. Since then, I’ve followed every generation of these tools: from the early Copilot versions through Cursor to Claude Code, OpenAI Codex, and the GitHub Copilot Coding Agent. Along the way I’ve kept up with every new release, every new model, and every new architecture.

One insight cost me the most time and frustration until I accepted it: The model is rarely the problem. Most of the time, it’s the context.

What “Context” Really Means

In the world of language models, “context” is often equated with the “context window”, the number of tokens a model can process at once. 128k, 200k, soon a million. The numbers keep growing, but the problem remains.

Because context is not only what the model can see but above all what it is supposed to pay attention to. Those are two different things.

A recent paper by Distyl AI (“How Many Instructions Can LLMs Follow at Once?”) looked at exactly that. The research team gave 20 different models tasks with an increasing number of concurrent instructions: from 10 to 500. The results finally gave me an explanation for something I had been observing for two years but could never quite put into words:

Even the best model tested (Gemini 2.5 Pro) reached only 69% accuracy at 500 instructions. The instructions fit easily into the context window. The model sees them all but does not follow them all.

Admittedly, the benchmark is simplified. Working keywords into a text is different from observing architectural rules in a codebase. But the principle carries over: if you give an agent an instruction file (CLAUDE.md, AGENTS.md, or a comparable format) with 30 project rules, plus 15 files as context and a complex task description, you are sending hundreds of implicit constraints into one interaction. The model can read them all. The question is whether it takes them all into account at the same time.

The context window determines how much the model can hold. Whether it follows all the instructions is a question of attention, and more memory doesn't help if attention is limited. A context window of one million tokens is like a desk three meters long. You still can't read all the documents on it at the same time.

Three Ways to Forget

The paper identifies three patterns of how models degrade under increasing instruction load. Anyone who works regularly with agents will recognize them all.

Threshold Decay: The model works almost perfectly up to a certain threshold, then performance drops off abruptly. Reasoning models like o3 or Gemini 2.5 Pro show this pattern. In practice, this was the most frustrating case for me: the agent performs excellently through the first 15 tasks. On task 16, it suddenly forgets the architectural requirements from the instruction file. It feels like losing a reliable colleague who, out of nowhere, starts doing things you never discussed.

Linear Decay: Steady, even deterioration with each additional instruction. Claude 3.7 Sonnet and GPT-4.1 behave this way. For a long time this was the most insidious for me, because I didn’t notice it. The quality erodes, like a conversation that slowly drifts off topic without anyone being able to pinpoint the moment it happened.

Exponential Decay: Rapid collapse even at moderate instruction density. Smaller models like Llama 4 Scout or Claude 3.5 Haiku are affected. Usable for simple, focused tasks, but not for complex projects.

What has preoccupied me the most: The dominant error type at high instruction density is omission. The model simply leaves instructions out, silently and without any hint. It hallucinates nothing wrong in their place, it just skips them. Anyone who has experienced this knows how it feels: you review the code, everything looks clean, and only on the third look do you notice that the error handling you explicitly asked for is missing.

Two Years of Evolution

Why the current generation is so much better only becomes clear when you compare it with the beginnings. That’s also when you see which problems have been solved and which have just been postponed.

2024: The Era of Manual Context Management

Two years ago, a typical workflow looked like this: you opened a chat, fed the model code snippets, explained the context in prose, formulated the task, and hoped for the best. With every new message, you had to drag the context along or risk the model forgetting it.

Cursor was an early breakthrough because it automatically included the editor context. You no longer had to explain to the model which file you were in. But the architectural level had to be re-established in every session: why the code is structured the way it is, which patterns apply, what the project conventions are.

The result: You became a context manager. Half of the working time went into keeping the context alive and was missing from the actual task. Prompt engineering was, at its core, context management, and it was exhausting.

2025–26: Context as an Architectural Feature

The current generation has understood that context management is an architectural problem and not a user problem. Every tool solves it in its own way.

Common to all current CLI agents is the concept of a persistent instruction file in the project root: CLAUDE.md in Claude Code, AGENTS.md in OpenAI Codex and Copilot CLI, and .cursorrules in Cursor. The agent loads the file automatically with every interaction. It contains project rules, conventions, patterns, technical decisions, everything the agent needs to know about the project.

Claude Code goes a step further with a Memory system: the agent can remember things that apply across sessions. “Always use bun instead of npm.” “The project uses Vitest, not Jest.” “API authentication runs via JWT with RS256.” Once said, it stays stored for good.

Claude Code also has Plan Mode. Before the agent writes code, it creates a plan that the human has to approve. In practice, this has changed my workflow: it forces the agent to make its context explicit. You see what it has understood and what it hasn’t, and you can correct it before a single line of code exists. The “Wait, I meant something different” now happens before implementation. That saves time and spares you the frustration of tearing out finished code.

Other tools are experimenting with an approach I find particularly promising: structured task management directly inside the agent. Tasks move out of the chat’s running text and into a local database, with status, dependencies, and progress. The agent can query what it has already completed, what is still open, and what is blocked. A chat with a memory turns into an agent with a backlog.

Regardless of the specific implementation, this approach solves a problem that constantly bothered me in practice: losing track of the work in progress. In the chat-based world, a context switch was often the moment the agent forgot the actual plan: a compiler error, a lint issue, a failing test. It fixed the error but could no longer reliably return to the original task. Persistent task management makes the work state independent of the conversation, whether it’s based on SQLite, Markdown files, or some other format.

The Real Problem: Large Existing Applications

Greenfield is comparatively easy.

You start from scratch, define the architecture, and build it up step by step. The context is manageable because there isn’t much context at the start. Agents shine here. That’s why most demos look so impressive.

The reality of professional software development looks different, and anyone who knows it knows this: You work in existing systems. Systems with 500,000 lines of code, grown structures, and implicit conventions, plus historical decisions that nobody has documented anymore but which still had their reasons. Systems that run in production and whose behavior you must not break.

Here, context becomes the bottleneck, and that has nothing to do with the agent’s intelligence: the relevant context is so large that even a human needs days to get up to speed.

I was tasked with implementing a new module in an existing enterprise application. The module had to work with the existing authentication system and use the existing role and permission model. It also had to fit into the existing CI/CD process and use the established patterns for database access, logging, and error handling.

Put simply: “Add feature X.”

In reality: a context problem with 47 implicit constraints, none of which the agent knows unless you brief it.

My first attempt (hand on heart) was to throw the whole folder at the agent and describe the task. “Here’s the code, here’s what I want, go ahead.” The result was working code that ignored every single project convention: its own logging framework in place of the existing one, its own error handling, its own database abstraction. Technically correct and architecturally a disaster. Anyone who has been through this knows the feeling: the code does the right thing, but it doesn’t belong.

That is the omission problem from the paper: the agent had all the information in the context window but didn’t take it all into account.

My Path Back to Structure

In a previous post, I described why AI-assisted development needs more planning. There, I also recommended doing the initial architecture work without the agent. Working in large existing codebases, the opposite has since proven better for me: planning together with the agent. The agent can systematically explore a codebase that even I don’t fully know. It scans directory structures, identifies patterns across hundreds of files, and uncovers dependencies I would have missed.

What I formulated there as a principle has since condensed into a concrete workflow that I use for every larger project.

The workflow looks old-fashioned, but it follows directly from what we know about context degradation. It is also surprisingly fun once you get into it.

Phase 1: The Planning Session

Before the first line of code, I run a thorough planning session with the agent, as a dialogue. That’s the part I enjoy most.

The session begins with the agent systematically examining the existing codebase: directory structure, architectural patterns, data models, dependencies, test structure, CI configuration. I instruct it to ask questions. Good agents do this on their own, and the best ones ask the right questions.

Then we discuss. I bring my architectural knowledge, the agent its pattern knowledge. We compare the existing code with best practices and look for the places where it deviates from established patterns, along with the question of whether these are conscious decisions or inconsistencies that grew over time. We try out ideas and discard them again or sharpen them.

It feels like pair programming with a colleague who has read all the documentation in the world but has never worked in this particular building. You know the building. Together, that produces something neither of you would have on your own.

The result is a joint, agreed plan, and it is concrete: clear architectural decisions, defined interfaces, identified risks.

This phase takes an hour, sometimes two. It took me a long time to stop seeing that time as a delay. Now it’s the most valuable part of the whole process for me.

Phase 2: Epics and Tickets

This is the step that made the biggest difference for me. I break the agreed plan down into epics, again together with the agent. Each epic is a self-contained, coherent block of work: “Extend database schema,” “Implement API endpoints,” “Build frontend components,” “Write integration tests.”

I split the epics further into tickets. A ticket describes a single, focused task with:

I store these tickets as Markdown files in a local directory, organized by epic. Agent and human can both read them, and they serve as the shared contract.

Immediately after creating the epics, I update the agent’s instruction file (the CLAUDE.md, AGENTS.md, or the respective equivalent). All architectural decisions, conventions, and patterns from the planning session go into the file now, before the first line of code. This way, the agent starts the implementation not only with a plan but also with an updated set of rules that keeps its work consistent across all epics.

An example of a ticket:

# EPIC-02/TICKET-03: Extend User Service with role verification

## Context
- Existing UserService in `src/services/user.service.ts`
- Role enum in `src/types/roles.ts`
- Existing pattern: Guards use `@UseGuards(RoleGuard)`
- Existing tests in `src/services/__tests__/user.service.spec.ts`

## Task
- Add method `hasPermission(userId, permission)` to UserService
- Respect existing role hierarchy from `roles.ts`
- Cache permissions per request (not globally)

## Acceptance Criteria
- [ ] Method exists and is typed
- [ ] Unit tests for all role combinations
- [ ] Existing tests continue to pass
- [ ] No new logging pattern introduced

## Dependencies
- EPIC-02/TICKET-01 (Role enum extended)
- EPIC-02/TICKET-02 (Database migration)

Phase 3: Epic by Epic

I assign the agent a complete epic, with all its tickets. The epic gives it place and structure: which part of the system is affected, which files are relevant, what the goal at the end should be. The tickets within the epic give it the individual tasks within that structure, in the right order, with clear dependencies.

The feeling when an agent works through a cleanly structured epic ticket by ticket and everything comes through the pipeline green at the end is fantastic. It’s like building with Lego where you would otherwise be untangling spaghetti.

This works because the epic provides the context and the tickets keep the instruction density per step low. The agent only needs to understand this epic, and it doesn’t need the whole project in its head for that. Within the epic, each ticket has 5–10 clear constraints and not 50 or 500, a number well below the degradation threshold the paper describes.

Context switches caused by compiler errors, lint issues, or failing tests stay local within the epic. The agent fixes the error and returns to the next ticket because the ticket list is the anchor. It’s there in black and white, independent of the conversation, so nothing drifts and nothing slips out of memory unnoticed.

If a ticket within the epic doesn’t work out, the error is contained. You debug a single, clearly defined task within a known context. That’s a different way of working from “the agent broke something somewhere in 2,000 lines and I get to go looking for it.”

Phase 4: Continuously Consolidate Knowledge

The instruction file isn’t done after the first draft. New insights keep surfacing during implementation: quirks of the test framework, implicit API conventions, patterns that only become visible while working in the code.

After every completed epic, I instruct the agent to update the instruction file and, in Claude Code, the memory as well, sometimes after individual tickets too, if something surprising has come up.

This is the moment when temporary knowledge becomes persistent knowledge. If the agent has discovered that a particular test framework has a quirk, that goes into the instruction file. The same applies to an undocumented API convention it has run into, or to a pattern the existing code follows consistently but which was never written down anywhere.

Without this ongoing step, knowledge erodes with every new session. Done this way, an increasingly rich contextual foundation builds up over time, one that noticeably speeds up the start of every new session.

The ticket system is a pragmatic response to an empirically proven problem: models degrade under instruction load. Fewer instructions per interaction, but more structure around them. For me, this has proven to be the most stable architecture for keeping frustration low and the quality of results high.

What Is Shifting

In classic software development, one person was responsible for architecture, planning, and implementation at the same time. Agile tried to merge these roles: everyone plans, everyone codes, everyone tests. That worked as long as people wrote everything themselves and learned in the process.

With coding agents, I observe these roles separating again, into a new configuration and not back to the old waterfall:

People are shifting toward architecture and planning. They understand the system, make the decisions, define the tasks, and check the results. They write less code in the sense of characters in a file, but they think more code than before, because designing the plan requires a deeper understanding than typing alone ever did.

The agent takes on more of the implementation. It writes the code, debugs the errors, and iterates against the pipeline. It is fast and precise and doesn’t tire, as long as its context is right.

Tickets become the interface between the two. That interface is written and structured, and it persists, unlike a verbal instruction, a chat history, or the fleeting context of a prompt.

I’ve used this workflow with Claude Code, with OpenAI Codex, and with GitHub Copilot CLI, and it works with every tool. The tools aren’t alike, but the workflow targets the problem they all share: the limited ability of language models to reliably follow many instructions at once.

What I’ve Learned from This

When I distill my two years with coding agents in existing codebases, I arrive at five things that made the difference:

1. Keep the instruction density per interaction low. One ticket with one task and clear context. The opposite would be “implement the whole feature.” Less is more reliable, and the research bears that out.

2. Make context persistent. Instruction files (CLAUDE.md, AGENTS.md), memory files, project documentation. Everything the agent needs to know across sessions belongs in files, because chat histories are fleeting.

3. Plan before implementing, together with the agent. The planning session is the phase in which human and machine build a shared understanding, and therefore not a waste of time. Without that understanding, every implementation is a guessing game.

4. Use tickets as a contract. Written, structured, with context and acceptance criteria. The ticket is the anchor the agent returns to when a context switch distracts it.

5. Consolidate knowledge after every milestone. What the agent has learned has to flow into persistent files. Otherwise every new session is a cold start.

Craftsmanship Means Mastering the Context

In my first post, I wrote that software craftsmanship shows in the last 10%. In the second, I asked who can still recognize those 10%, and in the third I argued that AI-assisted development needs more planning.

This post is the practical consequence: Craftsmanship in the era of coding agents means mastering the context. Mastering the model isn’t required for that, since it gets better every few months anyway. What’s meant is the ability to structure your own knowledge so that a machine can reliably implement it.

This is a new skill, one that didn’t exist three years ago and that is harder than it looks.

For a long time, I hoped the next generation of models would solve the context problem. Now I believe waiting isn’t worth it. Context management is a property of probabilistic systems and not a bug in today’s models. Even if the context window grows to ten million tokens and the models get significantly better once more: the ability to follow an arbitrarily long list of instructions at once and without error is not what Transformer architectures deliver. The solution lies more in the process than in the model.

This is less glamorous than “10x developer through AI.” But it’s what works in practice. At least in mine.

The best tool is useless if you hand it the wrong drawing, and a drawing that shows everything at once is nothing but noise. My way through the jungle was: small drawings, one sheet at a time.

AI Software Engineering Workflow Architecture Agentic Coding