Agentic Development Part 2: Anatomy of a Coding Agent
Part 1 introduced the basic agent loop: plan, act, observe, repeat until the goal is reached. This loop looks simple in a diagram, but behind it sit four distinct components, each with its own way of failing — and understanding these components one by one is the prerequisite for designing good tasks (Part 3), providing the right context (Part 4), and building effective guardrails (Part 5). This article dissects the anatomy of a coding agent: the planner that arranges the plan, tool use that acts as the agent’s “hands”, memory that manages what the agent remembers throughout a working session, and observation that verifies results before moving to the next iteration. In production, an agent isn’t just a prompt called once — it’s a distributed system where a language model happens to play the role of planner and executor, and like any distributed system, its reliability comes from architecture, not from how smart a single component happens to be.
Core Component: The Planner
The planner is the component that translates a high-level goal — whether a full spec from the previous series or a short instruction — into a sequence of concrete, executable steps. This isn’t just “the agent thinking about what to do”, but a process with structure: understanding the current state, comparing it with the end goal, and charting a path from one to the other.
There’s an important difference between two planning styles that often get mixed together in discussions about agents:
Static plans — drawn up once at the start, then executed step by step without revision, even when conditions on the ground change. Static plans fit tasks that are very well-defined and contain no surprises, but they’re fragile the moment an initial assumption turns out wrong. A plan built on the assumption “the database schema looks like this” will keep executing against that assumption even when step three discovers the schema is actually different.
Dynamic plans — redrawn or adjusted every time relevant new findings appear. Once an execution step reveals unexpected information, the planner re-evaluates whether the remaining plan is still valid, and adjusts if needed. This is what makes an agent feel “smart” compared to merely running a script — the difference isn’t in language ability, but in the ability to revise a plan based on new observations.
flowchart TD
A[Receive Goal] --> B[Understand Current State]
B --> C[Draft Initial Plan]
C --> D[Execute Step]
D --> E{New Findings Change Assumptions?}
E -- No --> F[Continue to Next Step]
E -- Yes --> G[Revise Plan]
G --> D
F --> DFor complex tasks, planners often work hierarchically — rather than drawing up one flat list of steps, they break a large goal into sub-goals, each of which only gets its detailed step plan when its turn arrives. This pattern is consistent with the spec-to-task-group decomposition covered in Part 4 of the Spec-Driven Development series — a hierarchical plan at the agent level is essentially a mirror of the hierarchical plan at the human-agent process level.
Recent research on agent architecture emphasizes the practice of explicitly separating the planning phase from the execution phase: the planner proposes a complete plan with constraints and success criteria, and only then does an executor run that plan under stricter tool restrictions. This separation improves control — the plan can be human-reviewed before execution (exactly the “plan mode” pattern hinted at in Part 4 of the previous series) — and reduces the blast radius of failures, because an error in the execution phase doesn’t automatically mean the entire plan must be rebuilt from scratch.
A good planner isn’t the one that produces the most detailed plan, but the one that knows when a plan needs revising. A sign of a weak planner: it keeps executing steps according to the initial plan even when mid-way observations clearly contradict the assumptions the plan was built on.
Core Component: Tool Use
A perfect planner without the ability to act is just talk. Tool use is the component that turns plans into real changes — reading and writing files, running shell commands, searching patterns in the codebase, calling external APIs, or using tools from a connected MCP server.
Some common tool categories for coding agents:
| Tool Category | Function | Example |
|---|---|---|
| File operations | Read and write file contents | Opening a file to understand it, writing code changes |
| Shell/bash execution | Run system commands | Running tests, installing dependencies, database migrations |
| Search/grep | Find patterns or references in the codebase | Finding all callers of a function before changing it |
| API/MCP calls | Interact with external services | Calling an issue tracker API, reading internal documentation |
| Version control | Manage code changes in a trackable way | Creating branches, committing, opening pull requests |
Tools aren’t just functions to call — tools are APIs that need to be designed with clear contracts, exactly like the API contracts covered in Part 3 of the Spec-Driven Development series. Every tool should validate inputs and outputs, be idempotent for side effects that can be safely repeated, and have clear time/cost limits. Loosely designed tools — accepting any input, failing silently without clear error messages — are a source of hard-to-trace agent failures, because the problem isn’t in the model’s reasoning but in an action layer that isn’t transparent about its own failures.
Choosing the right tool for a situation is also part of agent ability that’s often overlooked in general discussions. A good agent doesn’t immediately run a shell command to find something that’s faster and more accurately found via a dedicated search tool, and doesn’t rewrite an entire file when a one-line change would suffice. Choosing the wrong tool isn’t just about efficiency — a tool more invasive than needed (for example, running a destructive command when reading would suffice) increases risk without added benefit.
Tools with non-idempotent side effects — running the same command twice produces a different outcome than running it once — are a hidden risk in the agent loop. If the agent repeats a step because the previous observation was unclear, a non-idempotent tool can produce a corrupted state without the agent realizing it.
Core Component: Memory and Context Management
Memory controls what information is available to the agent while it works, and it’s one of the most misunderstood components. A common misconception is equating “memory” with a single mechanism like a vector database — but effective memory is a layered system, not a single layer.
Working memory (context window) — the information the agent actively uses in the current working session: instructions, contents of files being read, results of previous tool calls, and conversation history. This working memory is limited in size, and that’s the most tangible constraint shaping how agents work day to day — not a limitation of intelligence, but a limitation of how much information can be “held” at once.
Persistent memory (cross-session) — information that survives past the boundary of a single working session, usually stored as project convention files (like configuration files the agent reads at the start of a session to understand the project’s structure and rules), summaries of previous architectural decisions, or agreed preferences. Unlike working memory, which disappears when the session ends, persistent memory is what keeps the agent from having to “re-introduce itself” to the project from scratch in the next session.
Episodic memory — records of specific past events: what happened, when, and in what order. This is useful when the agent needs to understand change history — for example, why an approach was previously tried and rejected, so it doesn’t repeat the same mistake in a later session.
flowchart TD
subgraph Working["Working Memory - per session"]
A[Current Instructions]
B[File Contents Read]
C[Tool Call Results]
end
subgraph Persistent["Persistent Memory - cross-session"]
D[Project Conventions]
E[Previous Architecture Decisions]
F[Technical Specs: ERD, API Contract]
end
Persistent -.read at session start.-> Working
Working -.important findings saved back.-> PersistentThe diagram above shows why the technical specs from the Spec-Driven Development series — ERDs, API contracts, convention files — are functionally part of the agent’s persistent memory. The same specs discussed as “contracts for humans planning work” in the previous series, from an agent-architecture point of view, also serve as a context source read at the start of every session so the agent doesn’t have to re-guess already-agreed technical decisions.
Because working memory is limited, the strategy for managing what enters and leaves the context window becomes a skill of its own — often called context engineering. Some common strategies:
- Summarization — condensing conversation history or long exploration results into a compact version, so details that are no longer relevant stop eating context space
- Selective retrieval — loading only the codebase or documentation parts relevant to the current task, not the whole project at once
- Externalizing state — storing work progress (for example, task group status, as covered in Part 4 of the previous series) in external files, so if the context window needs to be “reset” mid-way through a long session, the work state isn’t lost with it
A full context window isn’t just about capacity — relevant information can get “buried” under irrelevant information, making it hard for the agent to find important details even though the information is technically still in context. Managing what enters the context is as important as managing its size.
Core Component: Observation and Self-Verification
Observation is the component that’s most often underestimated, even though it determines whether the agent loop actually learns from its actions or merely lurches forward blindly. Observation means the agent reads and interprets the results of the action it just took — the output of a run command, test results, the difference between expected and actual state — before deciding the next step.
The difference between an agent that observes well and one that doesn’t is clear in a simple scenario: the agent runs a test, and the test fails.
ANTI-PATTERN (shallow observation):
The agent runs the test command, sees there's some output (whatever it is),
treats "it ran" as a sign the task is done, and moves on to the next step
without reading whether the test passed or failed.
CORRECT (observation with verification):
The agent runs the test command, reads the exit code and the output content,
identifies which test failed and why, then adjusts the plan to fix the
failure before continuing to the next step.
Effective self-verification doesn’t stop at “did the command run without errors”, but reaches “is the result actually what was intended”. This connects directly to the issue covered in Part 5 of the Spec-Driven Development series — code can technically pass tests yet still drift from intent. Good observation checks against the relevant acceptance criteria, not just against “was there an error in the console”.
flowchart LR
A[Execute Action] --> B[Read Output/Result]
B --> C{Matches Acceptance Criteria?}
C -- Yes --> D[Continue to Next Step]
C -- Unclear --> E[Dive Deeper / Additional Tools]
C -- No --> F[Revise Approach]
E --> C
F --> AShallow self-verification — treating “no error” as proof of “success” — is one of the most common sources of agent failures that stay undetected until much later. A command can run with zero errors while still producing wrong output, especially in cases like silent failure (an operation fails quietly without an exception), or results that are syntactically valid but logically wrong.
Full Architecture Diagram
Combining the four components above into one complete architectural picture, with technical specs from the Spec-Driven Development series as input flowing into the planner at the start and serving as the verification reference in observation:
flowchart TD
Spec[Technical Spec: Intent, Constraints,<br/>Acceptance Criteria, ERD, API Contract] --> Planner
subgraph Loop["Agent Loop"]
Planner[Planner: Draft/Revise Plan] --> ToolUse[Tool Use: Execute via File/Shell/API]
ToolUse --> Observation[Observation: Read & Evaluate Results]
Observation -->|Matches| NextStep{Goal Reached?}
Observation -->|Doesn't Match| Planner
NextStep -- Not yet --> Planner
end
Memory[(Persistent Memory:<br/>Project Conventions, Decision History)] -.available throughout the loop.-> Planner
Memory -.available throughout the loop.-> Observation
Spec -.verification reference.-> Observation
NextStep -- Yes --> Done[Task Complete]This diagram shows two paths for specs entering the agent architecture: first as initial input shaping the planner’s plan, second as the reference observation uses to judge whether action results really match intent — not just “looks successful” on the surface. Persistent memory provides the same project context to both components, keeping the decisions the planner makes and the standards observation applies consistent throughout the working session, even when the session runs long and involves many loop iterations.
Single Agent vs Sub-agents in One Task
Before getting into the full multi-agent orchestration topic covered in Part 6, there’s a simpler and more common daily pattern: within one working session, the main agent can delegate part of the work to more specialized sub-agents, then merge their results back into the main workflow.
The difference from full multi-agent orchestration is in scale and independence: sub-agents within one task are usually short-lived, created for one specific sub-job, then their results are immediately absorbed back by the main agent. The multi-agent orchestration covered in Part 6 involves multiple agents each with independent roles running for longer, often with a separate coordinator agent managing the whole flow.
When the sub-agent-within-one-task pattern helps:
- Independent parallel exploration — for example, searching for usage patterns of a function across many parts of the codebase at once, where each search doesn’t depend on the others
- Sub-tasks needing narrow focus — the main agent delegates analysis of one complex file to a sub-agent whose context is limited to that file, so its analysis is sharper without being “distracted” by the whole codebase
- Keeping the main agent’s context window lean — long exploration results from sub-agents are summarized before being returned to the main agent, preventing the main context window from filling with exploration details that aren’t all relevant to the next decision
When a single agent is enough, without sub-agents:
- Tasks with a small scope that don’t require broad exploration
- Strictly sequential work, where every step depends fully on the previous step’s result — parallel delegation here brings no benefit and only adds coordination complexity
Consider sub-agents when the work has parts that can be explored independently and whose results can be summarized into something compact for the main agent to consume. If the results still need full detail to be understood, the sub-agent benefit shrinks because the context saved on one side comes back bloated when the full results return.
Common Failures in Each Component
Understanding the components separately also makes it easier to diagnose where a failure actually occurs when an agent produces poor output — instead of blaming “the AI isn’t smart enough” in general.
Failures in the Planner
- Plans that are too shallow — jumping straight to execution without really understanding the current state, producing steps based on unverified assumptions
- Plans that are too deep — spending too many reasoning rounds on a task that’s actually simple, slowing execution without added benefit
- Not revising the plan despite new findings — the case mentioned above, a static plan forced through even though conditions have changed
Failures in Tool Use
- Choosing the wrong tool for the situation — running an invasive shell command when a safer search tool would suffice
- Not handling tool errors well — the tool fails to run, but the agent continues as if it succeeded because it didn’t explicitly check the success status
- Repeatedly running non-idempotent side effects — as discussed above, risking a corrupted state when the agent repeats a step because of an unclear previous observation
Failures in Memory and Context
- Context overflow — important information gets “squeezed out” because the context window is full of less relevant detail, especially in long sessions without a good summarization strategy
- Important information lost between sessions — architectural decisions or project conventions that should be persistent but were never saved to persistent memory, so they have to be “rediscovered” every time a new session starts
- Stale memory — conventions or decisions stored in persistent memory are no longer relevant because the project changed, but are still used as references without being updated — exactly the same problem as stale specs covered in Part 4 of the previous series
Failures in Observation
- Shallow verification — treating “no error” as equivalent to “correct result”, without checking against the actual acceptance criteria
- Not digging deeper when results are ambiguous — an unclear observation should trigger additional exploration, not be assumed fine and continued past
- Ignoring subtle failure signals — like silent failures or results that are technically valid but logically wrong, which need more than just an exit code check
The most dangerous failure isn’t the one that produces an obvious error — that’s easy to detect and fix. The most dangerous failure is the one that passes all shallow checks but is still wrong in substance, because it can propagate through several loop iterations before finally surfacing, exactly like the compounding errors covered in Part 1.
Summary
- An agent in production is a distributed system with four core components — planner, tool use, memory, and observation — not a single prompt that happens to be smart
- The planner translates goals into concrete steps; dynamic plans that can be revised based on new findings are far more resilient than static plans forced through even after their assumptions are proven wrong
- Tool use is the agent’s “hands” — tools need to be designed with clear contracts, be idempotent for side effects, and the agent needs to pick the right tool (no more invasive than needed) for each situation
- Memory isn’t a single mechanism but a layered system: working memory (per session, limited size), persistent memory (cross-session, including technical specs from the previous series), and episodic memory (records of past events)
- Observation determines whether the loop truly learns from its actions — shallow verification (“no error” treated as “success”) is the most common and hardest-to-detect source of failure
- Technical specs (ERDs, API contracts, acceptance criteria) flow into the agent architecture at two points: as initial input for the planner, and as a verification reference for observation
- Sub-agents within one task help with independent parallel exploration and keeping the main agent’s context window lean — different in scale from the full multi-agent orchestration covered in Part 6
- Each component has its own failure modes; diagnosing failures per component is more productive than blaming “AI intelligence” in general