When models got capable enough to follow instructions, the bottleneck moved from wording the prompt to curating exactly what the model knows, sees, and remembers at the moment it acts.
A transformer is a function from a token sequence to a probability distribution over the next token. It has no memory, no state, no awareness of your database — it sees exactly the tokens you assemble into its context window and nothing else. Prompt engineering asked: how do I word the instruction so the model does the thing? Context engineering asks the harder, more general question: of everything the model could see — instructions, tools, retrieved documents, conversation history, scratchpad notes, prior reasoning — what subset do I place in the window, in what order, at what point in the task, and which agent gets to see it? The shift happened because frontier models in 2026 are good enough at following clear instructions that cleverness in wording yields diminishing returns; the dominant failure mode is no longer "the model misunderstood me" but "the model didn't have the right information, or had too much of the wrong information, at the moment it had to act." Context is now the scarce, expensive, latency-bearing resource you budget — and engineering it is a curation problem, not a wording problem.
The words first.
Step by step.
Remember this: prompt engineering wrote the sentence; context engineering curates the entire window the sentence lives in.
Start from first principles. A decoder-only transformer computes P(next_token | token_1, ..., token_n). It carries no hidden state between API calls; the API is stateless. So when an agent "remembers" your name across turns, that is not memory inside the model — it is your harness re-sending the prior turns in the context window every single call. This single fact is why context engineering exists: the model's entire epistemic state on call k is the token sequence you assembled for call k. Nothing else.
Prompt engineering treated that sequence as mostly fixed — a system prompt plus the user's question — and optimized the wording. That was the right bottleneck when models were unreliable instruction-followers (GPT-3.5 era): a better-phrased prompt was the difference between working and not. But as models crossed a capability threshold, wording stopped being the binding constraint. The binding constraint became composition: which of the millions of possible tokens you assemble, given a finite window, finite attention, real dollars per token, and real milliseconds of prefill latency.
Using 26,000 of 32,000 tokens (81%). Within budget. Every token you spend on retrieved context is one you can't spend on history or output — that tradeoff is the job.
Context engineering decomposes into four orthogonal decisions. Memorize these — they are the spine of the discipline and the spine of the interview.
1. What — knowledge, tools, and history. Which instructions, which retrieved documents, which tool definitions, which slice of conversation history. The core insight from Anthropic's engineering guidance: find the smallest set of high-signal tokens that maximizes the likelihood of the desired behavior. More is not better. A bloated tool set with overlapping functions makes tool selection worse, not better; ten carefully chosen documents beat a hundred mediocre ones. "What" is a curation and ranking problem, which is exactly why retrieval and reranking (see /rag) are part of context engineering.
2. When — timing. Does a piece of knowledge belong in the window from token zero, or should it be fetched just-in-time when the task demands it? Pre-loading everything is the naive default and it is usually wrong: it fills the window with tokens that are irrelevant to 90% of requests. The alternative is just-in-time retrieval — keep lightweight identifiers (file paths, IDs, glob patterns) in context, and load the actual content via a tool call only when needed. This trades a round-trip of latency for a dramatically smaller, higher-signal window. Claude Code works this way: it doesn't pre-read your repo, it greps and reads on demand.
3. How much — the budget. Every token has three costs: dollars (input-token price), latency (prefill scales with prompt length), and quality (context rot — section 3.4). The window is a budget you spend, not a container you fill. Context-aware models now expose this explicitly: Claude Sonnet 4.6 and Haiku 4.5 track a running token budget (Token usage: X/1M remaining) updated after each tool call, so the model itself can pace a long task and avoid premature shutdown. At the harness level, "how much" is enforced by compaction, context editing, and summarization — mechanisms that actively remove tokens to stay within budget.
4. Visibility — per-agent scoping. In a single-agent system there is one window and everyone sees everything. In a multi-agent system you choose what each agent sees. A sub-agent doing a focused search task should get a minimal window — its instructions and the search tools — and return a condensed 1–2K-token summary to the orchestrator, not its entire noisy 50K-token exploration transcript. Visibility is the most advanced lever and the one IC6 candidates are expected to design with: it lets you parallelize, isolate failure, and keep each window small, at the cost of coordination complexity and the risk that an agent lacks context it needed.
These four are not independent knobs you can max out individually — they trade against each other. Loading more (what) raises cost (how much). Just-in-time loading (when) adds latency but shrinks the budget. Isolating agents (visibility) shrinks each window but risks dropping needed context. The discipline is navigating these tradeoffs deliberately.
Context engineering is not only about what tokens — it is about their order, because of prefix caching. Providers cache the KV tensors for the front of your prompt. On Anthropic's API a cache hit costs 0.1× the base input price (a 90% reduction) and a cache write costs 1.25× (5-minute TTL) or 2.0× (1-hour TTL). The render order is fixed: tools → system → messages. The invariant is brutal and absolute: caching is a prefix match — any byte change anywhere in the prefix invalidates everything after it.
This converts a design principle into an economic one. Put stable content first (frozen system prompt, deterministic tool list, sorted JSON) and volatile content last (the user's varying question, timestamps, per-request IDs). A datetime.now() interpolated into your system prompt header silently destroys caching for the entire prompt — you pay full price on every call and never see an error, just cache_read_input_tokens: 0. For agents this also dictates architecture: don't change tools or swap models mid-conversation (both invalidate everything); if you need a cheaper model for a sub-task, spawn a sub-agent rather than switching the main loop's model.
Symbols in plain words: base = the normal per-token input price. A cache write costs 1.25 × base (you pay a 25% premium to store the prefix, 5-min TTL). A cache hit costs 0.1 × base (90% off, because the provider reuses stored computation). N = number of requests sharing the same prefix.
Concrete: a coding agent with a 100,000-token stable prefix (system prompt + tool defs + a loaded file), Opus 4.8 input at $5 / 1M tokens, so base for this prefix = 100,000 / 1,000,000 × $5 = $0.50.
10 × $0.50 = $5.00.1.25 × $0.50 = $0.625), next 9 read (9 × 0.1 × $0.50 = $0.45). Total = $1.075.$1.075 / $5.00 ≈ 0.215 → about a 79% cost reduction, and every cached request also skips re-prefilling 100K tokens, cutting latency. What it did to the data: the exact same tokens now cost a fifth as much — purely by keeping the prefix byte-identical so the cache matches. Break-even on 5-min TTL is just two requests (1.25 + 0.1 = 1.35 < 2.0).
Two empirical facts explain why the paradigm flipped.
First, models got good at instructions. When the model reliably does what a clear instruction says, the marginal return on rewording collapses. You no longer need the "you are an expert, take a deep breath, I'll tip you $200" incantations of 2023. A plain, well-structured instruction works. So the lever with leverage moved elsewhere — to what surrounds the instruction.
Second, bigger windows did not make curation optional — they made it more important. This is the counterintuitive part that interviewers love. With 1M-token windows you might think "just stuff everything in." But attention is an n² operation over pairwise token relationships, and the model has a finite attention budget. Two robust findings:
The mitigations are pure context engineering: retrieve before you stuff (use RAG + reranking to bring only the top documents in), place critical information at the boundaries (start or end, never buried in the middle), and reorder retrieved documents so the highest-relevance ones land at the highest-attention positions. The benchmark that exposes models that only have a big window but no engineering discipline is MRCR v2 (multi-round co-reference resolution) — it embeds duplicate prompts among distractors and asks the model to retrieve a specific instance; at 1M tokens with two needles, even strong models score well below their single-needle numbers. Window size alone does not buy you long-context performance; placement, compression, and retrieval strategy do.
The discipline is operationalized by named mechanisms. Mapping them to the levers makes the framework concrete:
/rag), minimal non-overlapping tool sets, skills (load full instructions only when relevant).And a fifth, cross-cutting lever that the four dimensions all interact with: reasoning-time compute. Extended/adaptive thinking lets the model spend tokens reasoning before answering — quality improves with budget but flattens above roughly 10–32K thinking tokens, with linear cost. This is "what" (the reasoning is context the model produces for itself) and "how much" (you budget it via effort levels) at once. Crucially, thinking blocks interact with caching: preserved thinking enables cache hits across turns; clearing thinking invalidates the cache. That coupling is exactly the kind of cross-dimension interaction a senior engineer is expected to reason about.
The cleanest way to feel context engineering is to build the same prefix two ways — one that caches and one that silently doesn't — and watch the usage numbers. This is runnable against the Anthropic API and is the single most common "why is my bill high" bug in production.
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from env
# A large, STABLE prefix: instructions + a loaded document. This is the
# "what" you've decided the model needs, and it must stay byte-identical
# across calls for the cache to hit.
STABLE_SYSTEM = (
"You are a support assistant for Acme Corp.\n\n"
"=== PRODUCT MANUAL (excerpt) ===\n"
+ ("Section policy text ... " * 4000) # ~stand-in for a real ~20K-token doc
)
def ask(question: str) -> anthropic.types.Message:
return client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
# cache_control on the system block: render order is tools -> system
# -> messages, so a breakpoint here caches the whole stable front.
system=[{
"type": "text",
"text": STABLE_SYSTEM,
"cache_control": {"type": "ephemeral"}, # 5-min TTL, 1.25x write / 0.1x read
}],
# The volatile part — the varying question — goes LAST, after the
# cached prefix, so it never invalidates the cache.
messages=[{"role": "user", "content": question}],
)
questions = [
"What is the refund window?",
"How do I reset my password?",
"Is there an enterprise SLA?",
]
for i, q in enumerate(questions):
r = ask(q)
u = r.usage
print(
f"[{i}] write={u.cache_creation_input_tokens:>6} "
f"read={u.cache_read_input_tokens:>6} "
f"uncached={u.input_tokens:>4}"
)Run it and you'll see request [0] pay the write (cache_creation_input_tokens is large, cache_read is 0), then requests [1] and [2] flip to large cache_read and near-zero cache_creation — the 90%-off path. Now introduce the classic bug: change STABLE_SYSTEM to interpolate f"Current time: {datetime.now()}\n" at the top. The byte at the front changes every call, the prefix match fails, and cache_read_input_tokens stays 0 forever — you've silently turned off caching and 5×'d the cost of this prefix, with no error to alert you. That is context engineering in miniature: a single placement decision (stable-first vs. volatile-first) is worth ~79% of your input bill. To verify a real prompt, assert usage.cache_read_input_tokens > 0 across repeated calls in a test; if it's zero, diff the rendered prompt bytes between two requests to find the invalidator.
| Mechanism | Primary lever | Cost | Latency | Quality effect | Main failure mode |
|---|---|---|---|---|---|
| Prefix caching | how much / order | Write 1.25–2.0× once; reads 0.1× | Big drop on hits (skip prefill) | Neutral (same tokens) | Silent invalidation → 0 hits, no error |
| Just-in-time retrieval | when | Pays per tool round-trip | +1 round-trip per fetch | Higher signal density | Model fails to fetch; needed doc never loaded |
| RAG + reranking | what | Embedding + rerank compute | Retrieval adds ~10–100ms | Big win vs. stuffing | Wrong docs retrieved; lost-in-the-middle if unordered |
| Compaction | how much | Two model passes (summarize + continue) | Adds a summarization call | Lossy — fact-dense detail dropped | Summary omits a load-bearing detail |
| Context editing | how much | Cheap (deletes blocks) | Negligible | Keeps transcript lean | Clears a tool result the model still needed |
| Extended/adaptive thinking | reasoning compute | Linear in thinking tokens | Higher TTFT / total time | Improves hard reasoning, flat above ~10–32K | Overthinking; cost with no quality gain |
| Sub-agent isolation | visibility | Extra agent calls + coordination | Parallelizable | Each window small & focused | Sub-agent lacks context; coordination bugs |
The prose that matters: compaction is lossy and that is unavoidable — summarization cannot match full-context precision for fact-dense content (legal clauses, code, numeric tables). Prefer extraction with preservation rules (keep code blocks, technical decisions, and current state verbatim; summarize only narrative) over blanket summarization, and prefer context-editing (delete stale tool results, keep the structure) where you can, because deletion is honest about what's gone while a summary can confidently hallucinate completeness. What changes at scale: at low volume, just pick a big model and stuff the window — engineering effort isn't worth it. At high volume or long horizons, the window becomes the dominant cost and quality driver: caching swings the bill by 5×, context rot swings accuracy by tens of points, and a 200K-token agent loop that never compacts will eventually hit the context limit mid-task and fail. The discipline is not optional past a certain scale; it is the system.
json.dumps() in the prefix kills caching with no error. Always verify cache_read_input_tokens > 0 across repeated calls.Flashcard. Context engineering = choosing what (knowledge/tools/history), when (timing), how much (budget), and who sees it (per-agent visibility) — because the model is a stateless function over the window you build, and a capable model rewards curation over clever wording.
Next: Retrieval-augmented generation — the "what" dimension in depth: sparse vs. dense retrieval, reranking, and document ordering against lost-in-the-middle.