Ten design problems. Write your own design for each one, then compare it with one reasonable answer.
How to use these exercises
Each exercise describes a real job, such as a weekly report on competitors, and asks a few design questions. Write your answer in the box, then open the reveal and compare. The reveal shows one reasonable design, not the only one.
Each tool has an effect class from chapter 2, because the class decides how much checking a call needs. A read-only tool changes nothing. A reversible write changes something you can undo, such as a draft. "External" means the action leaves your system or cannot be undone, such as an email or a payment.
In the diagrams, each step says who does it. Steps marked (code) are deterministic: they run the same way every time. Steps marked (model) need judgment. The colors repeat this: violet for the model, blue for your code, green for tools and outside systems, amber for stored data, and rose for a person.
Start from the least agentic design that works (chapter 0): if the steps never change, code should run them. Some jobs below need no agent at all. When you finish, grade your design with the rubric at the end.
Competitor report every Monday
A product team tracks eight competitors. Every Monday by 7:00 they want a report on what changed last week across about 40 pages, such as pricing pages and release notes, plus news. Every claim must link to its source.
What tools does it need?
What state should persist?
Does it need memory?
Should it use subagents?
Does it need scheduling?
What should happen if one website fails?
How should success be evaluated?
Reveal one reasonable architecture
A deterministic workflow is enough, because the sources, the schedule, and the report format are known in advance. Code runs the steps in order, and the model only reads changed pages and writes the report.
Monday 06:00 (code)
|
v
For each source, in parallel (code):fetch --> compare withlast week's copy
| |
| failed | changed
v v
mark "not checked"extract facts and quotes (model)
| |
+-------+--------+
v
Choose what matters, write the report (model)
|
v
Check every quote against its page (code)
|
v
Post once for this week (code)
Tools
fetch_page(url)Read-only Code limits how often it calls each site.
search_news(company, since)Read-only It finds recent news.
save_copy(source, text)Reversible write It keeps each page for next week's comparison.
post_report(week, text)External It posts once per week.
Design decisions
State
A database keeps each page's last copy and a record per week: the facts, the draft, and whether it was posted.
Memory
Almost none: a short guide from the team says which changes matter.
Subagents
No, because a loop in code already gives each source its own model call.
Scheduling
Yes, it runs weekly. The week (for example 2026-W43) is the run's id, so a second trigger that week does nothing.
If one site fails
Code retries three times, then lists the page as "not checked" in the report, so a failure never looks like "no change".
Approvals
Posting is an external effect, because a wrong report misleads people once it is out. So a person approves each report until the evaluation shows few errors, and only then does the team choose to post automatically.
Security boundary
Fetches may reach only the listed sites and the news API, and code refuses redirects to other domains. Only the post step holds a credential, and it can post only to the team's channel. Because the model has no tools, a page with hidden instructions can at worst distort a summary.
Evaluation
Replay 20 past weeks that the team labeled, and count missed and invented changes.
Another design
An agent with web search can find new sources, but its coverage varies, so it fits a monthly run that suggests sources.
What could go wrong
A redesign makes a whole page look changed, so the model may invent a price change. Code sends very large changes to a person.
Coding agent: fix failing CI on pull requests
A monorepo gets 400 pull requests a day, and one in five fails CI, often for small reasons such as a lint error. The team wants an agent that pushes a fix or explains the failure.
What starts a run, and what stops it from triggering itself forever?
When may it push, and when should it only comment?
How do you stop it from weakening a test to make it pass?
Reveal one reasonable architecture
An agent runs once per CI failure. Code decides whether to run, the model works out a fix, and code decides whether the fix may be pushed.
CI failed (webhook)
|
v
Opted in? Under 2 tries? (code)
|
v
Rerun the job once (code) --> passes: label "flaky", stop
|
v
Agent loop in a fresh sandbox (model): read log, edit, recheck
|
v
Diff check (code): passes, small, no tests weakened
|-- ok -----> push one commit (code)
|-- not ok -> comment with the diagnosis (code)
Tools
get_ci_log(job)Read-only It returns the failing step's log.
edit_fileReversible write Edits stay in the sandbox until code pushes them.
run_check(name)Reversible write It reruns only the command CI ran, inside the sandbox. The agent has no general shell.
Design decisions
State
Code records attempts and outcomes per pull request and commit, so a duplicate trigger does nothing.
Memory
A short file per repo, written by people, lists safe fixes.
Subagents
No, because each failure is one small task.
Triggers
A webhook, which is an HTTP call that CI makes when a job fails. The agent's own commit also triggers CI, so code stops after two attempts.
Approvals
Authors opt in with a label, which counts as consent. Without it, the agent only comments with a suggested patch.
Security boundary
The sandbox has no secrets and reaches only the package registry, because pull request code may be hostile. Only the pushing code holds a token.
Evaluation
Replay 200 past failures with known fixes. A trial passes only if CI passes and the diff leaves tests and CI config alone.
Another design
Run deterministic fixers, such as the linter's auto-fix, before the agent, because they need no model.
What could go wrong
The author pushes new commits during the run, so the fix is based on old code. Code checks the branch before pushing and drops the fix rather than overwrite the author's work.
Customer support agent
An online store gets 2,000 support messages a week, mostly about orders and refunds. Refunds up to $100 within 30 days are automatic, and larger ones need a person. The agent should resolve what it safely can.
Which parts are a workflow, and which need an agent?
What does the model see for one conversation?
How might a customer trick it, and what stops that?
Reveal one reasonable architecture
Code routes each message, and a small agent handles anything beyond a status check. The refund limit lives in the refund tool, because only code can enforce a policy.
Message arrives (event)
|
v
Verify the customer, load their orders (code)
|
v
What is it about? (model, small and cheap)
|-- order status --> answer from a template (code)
|-- anything else --> support agent (model + narrow tools)
|
v big refund, legal, 3rd contact
hand off to a person
Tools
get_order(id)Read-only It returns only this customer's orders, because code takes the customer id from the login.
update_address(order, address)Reversible write It works only before the order ships.
issue_refund(order, amount)External It applies the refund policy itself and pays only to the original card.
send_reply(text)External Every message to the customer goes through it.
Design decisions
State
The helpdesk holds the customer, the orders, each action with its id, and the status. The agent keeps no copy.
Memory
None of its own. It reads the customer's history each time, and never stores a customer's claim as a fact.
Subagents
No, because conversations are short and independent.
Approvals
A support lead approves refunds over the limit, after seeing the order, the rule, and the exact refund.
Security boundary
The model sees this conversation, these orders, and the policy, but no card numbers or other customers. A fooled model can only request a refund, which the tool still checks.
Evaluation
Replay 500 past conversations against fake systems. A trial fails on any policy break or any action that no tool confirmed.
Another design
Forms and templates cover the common requests more cheaply. Many teams start there and add an agent later.
What could go wrong
A refund call times out, and the retry pays twice. So code sends an idempotency key, an id the payment service uses to ignore repeats.
Personal research agent
You want an agent that answers questions such as "Compare three vector databases for 50 million documents" with a short report and citations. A run can take 20 minutes. Over time it should learn what you know and which sources you trust.
Is this an agent or a workflow?
Would subagents help?
When should it stop?
Reveal one reasonable architecture
This needs an agent, because each search depends on what the last one found. A lead agent splits the question, and researcher subagents explore the parts in parallel. It stops when every part has notes with citations, or when the budget runs out.
Your question
|
v
Lead agent splits it into 3-5 parts (model)
|
+--> researcher 1 --+
+--> researcher 2 --+--> notes with quotes and links (files)
+--> researcher 3 --+
|
v
Lead agent merges notes, flags disagreements (model)
|
v
Check each quote against its source (code)
|
v
Report, plus suggested memory updates for you
Tools
web_search, fetch(url)Read-only Only researchers have them. Fetch saves each page to a file.
write_note(path, text)Reversible write It writes in this question's folder.
delegate(question, budget)Read-only Only the lead has it. It starts a researcher with read-only tools.
propose_memory(fact)Reversible write It adds a suggestion to your review list.
Design decisions
State
A folder per question holds the plan, notes, sources, and draft, so you can reopen it later.
Memory
A short profile you control holds your background, trusted sources, and preferred format.
Subagents
Yes, because the parts are independent and pages fill a context fast. Each researcher returns at most 500 words with quotes and links.
Security boundary
Pages can hide instructions, and a request can leak data in its URL. So researchers never see your profile, and fetch only opens links found in results.
Evaluation
Use 30 questions with known good answers. Code checks every quote against its source, and you grade the coverage.
Another design
A single agent is cheaper and fine for narrow questions. Add researchers when page text starts to fill its context.
What could go wrong
A summary drops a caveat. The notes keep the full quotes, so the lead can check.
Trusted sources crowd out everything else. So keep some outside results, and show what was left out.
Sales agent
A software company gets 300 demo requests a week. It wants an agent that researches each lead, scores the fit, writes a first email, and books a call with the right rep. It must respect email consent and never offer discounts.
Which steps are fixed, and which need judgment?
How do you stop it from inventing facts about a prospect?
Which numbers show it works, and which would fool you?
Reveal one reasonable architecture
This is mostly a workflow, because every lead goes through the same steps. The model drafts emails and explains unusual scores. Outbound email gets a gate, because a bad email cannot be unsent.
Demo request (event)
|
v
Remove duplicates, check consent, look up company (code)
|
v
Score fit: rules (code),unusual cases explained (model)
|-- poor fit --> polite template (code)
v
Draft an email from looked-up facts only (model)
|
v
Rep approves --> send and record in the CRM (code)
|
v
Replies --> reply agent: answers, books a call (model)
Tools
lookup_company(domain)Read-only It gets company facts from a data provider, each with its source.
save_draft(lead, text)Reversible write It saves a draft in the CRM, the sales team's customer database.
send_email(draft)External It runs after approval, and code blocks anyone who did not ask to be contacted.
book_meeting(lead, time)External It runs once the prospect picks a time.
Design decisions
State
The CRM holds the lead, the facts, the score, the draft, the approval, and the replies.
Memory
The agent loads a profile of a good customer and a writing guide, plus good past emails as examples.
Subagents
No, because each lead is a short pipeline. Leads run in parallel from a queue.
Approvals
Reps approve every first email for a month. After that, code holds any email or reply that mentions a price or a discount until a rep approves it.
Security boundary
There is no discount tool, so the agent cannot apply a discount. It could still promise one in plain text, which is why code holds pricing messages for a rep. The model sees one lead, not the whole CRM.
Evaluation
Score 200 past leads and compare with the reps' judgment. Code checks that every personal claim in an email matches a looked-up fact.
Another design
As a safer first step, the agent writes a brief and a draft, and the rep sends the email.
What could go wrong
The reply rate rises because people reply angrily. So count meetings held and complaints, not replies.
Browser agent
Your operations team spends hours each week in a supplier's web portal that has no API. They download invoices, compare them with purchase orders, and file disputes for mismatches. They want a browser agent to do the downloads and draft the disputes.
Which parts should the browser agent not do at all?
Can the model see the password?
What happens when the portal's layout changes?
Reveal one reasonable architecture
Split the job. The browser agent only finds and downloads invoices. Code compares them with purchase orders, because that is arithmetic, not judgment. The model drafts disputes, and a person submits them.
Weekly schedule (code)
|
v
Browser agent in a fresh, isolated browser (model)log in (code fills in the password)open invoices --> filter last week --> download each one
|
v
Downloads match the portal's invoice count? (code)
|
v
Compare with purchase orders (code)
|-- all match --> mark ready to pay
v mismatch
Draft a dispute (model) --> a person submits it
Tools
read_page()Read-only It returns the accessibility tree: the page's buttons, links, and text, as a screen reader sees them. This is smaller and more exact than a screenshot.
click(element)Depends on target Code blocks buttons such as Submit, Pay, and Delete.
download(link)Reversible write It saves the file to the run's folder.
login()Read-only It takes no arguments, so the model never handles the password.
Design decisions
State
Each run records the invoices listed and the files downloaded, so a rerun after a crash skips finished files.
Memory
A short guide, written by people, describes the portal's quirks.
Subagents
No, because one browser session is easier to follow and gentler on the portal's rate limits.
Approvals
A person submits every dispute. Code blocks dangerous clicks instead of asking, because constant questions teach people to approve without reading.
Security boundary
A fresh browser in a container can reach only the portal and has no real cookies. The portal account can draft disputes but cannot pay.
Evaluation
Record the portal's pages and replay them offline. A trial passes when the agent finds every invoice and tries no blocked click.
Another design
Ask the supplier for an export or an API first. If the portal rarely changes, a plain script with no model also works.
What could go wrong
The layout changes and the agent cannot find the invoices. It stops after 15 steps and alerts a person instead of guessing.
Data-analysis agent
Product managers ask questions such as "Why did weekly signups drop 12% last week?" and wait two days for an analyst. The company wants an agent that answers in chat by querying the data warehouse and showing its work.
What must the agent know before it writes a query?
How does a manager know whether to trust an answer?
When should it hand off to an analyst?
Reveal one reasonable architecture
This needs an agent, because each query depends on the last result. But every tool is read-only, each metric uses its approved definition, and every number in the answer comes from a query that ran.
Question in chat
|
v
Agent loop (model + read-only tools, at most 20 steps)look up metric --> write SQL --> run on a copy --> read result--> next query, until the answer holds up
|
v
Build the answer (code): copy numbers from results, attach SQL
|
v
Reply, labeled "approved metric" or "exploratory"
Tools
get_metric(name)Read-only It returns the approved definition, such as what counts as a signup.
search_catalog(term)Read-only It finds tables, columns, and known data problems.
run_sql(query)Read-only It runs on a read-only copy of the database, with limits on rows, time, and cost.
Design decisions
State
Code records each query with its SQL, row count, and data freshness. The model sees totals and sample rows, not full tables.
Memory
The agent uses the approved metric definitions, known traps such as "signups before March 3 count some users twice", and past answers with their SQL.
Subagents
Usually no, because each query depends on the last. One exception helps: a checker recomputes the headline number without seeing the first answer.
Approvals
It needs none, because no tool can write. Costly queries and metrics without an approved definition go to an analyst.
Security boundary
Queries run with the asker's permissions, so the agent cannot show anyone data they could not query themselves.
Evaluation
Use 100 questions with analyst-checked answers. Grade the number, the method, and whether the answer states its uncertainty.
Another design
One model call that writes SQL handles lookups such as "signups last week". But it cannot follow up, so it fails on "why" questions.
What could go wrong
A join counts rows twice and gives a believable wrong number. The recheck catches most of these.
The answer turns a correlation into a cause. So answers separate what the data shows from guesses about why.
Autonomous debugging agent
On-call engineers get paged 30 times a week, often for repeats such as a full disk or a bad deploy. The team wants an agent that starts investigating when an alert fires, posts findings in the incident channel, and may apply a few known fixes.
What may it do alone, and what needs a person?
How does it track hypotheses?
What counts as "root cause found"?
Reveal one reasonable architecture
The agent only reads by default, and it keeps a list of hypotheses with evidence for and against. A cause counts as found only when it explains the symptom and when it started, and a fix confirms it.
Alert fires (event)
|
v
Gather recent deploys and past incidents (code)
|
v
Investigate (model + read-only tools, at most 15 minutes)H1 bad deploy 4512 3 for, 0 againstH2 database load 0 for, 2 against
|
v
Post findings every 5 minutes (code)
|
v
Propose a fix: "roll back deploy 4512" (model)
|
v
On-call approves --> fix runs --> errors back to normal? (code)
Tools
query_logs, query_metrics, list_deploysRead-only Code caps their results and saves long outputs to files.
update_hypotheses(list)Reversible write It edits the list in the incident record.
propose_fix(action)External It offers fixes from a short approved list, such as a rollback. A separate service runs them after approval.
Design decisions
State
The incident record holds the timeline, the hypotheses with links to their queries, and the fixes tried, so everyone shares one view.
Memory
Code finds past incidents by symptom. Runbooks, the written procedures for known problems, each include a check that proves the fix worked.
Subagents
Not at first, but if incidents span many services, helpers can gather evidence in parallel.
Triggers
Code starts one investigation per incident, not per alert, because alerts arrive in bursts.
Approvals
Every fix needs approval. The request shows the evidence, the expected effect, and how to undo it.
Security boundary
The agent's credentials are read-only. Only the fix service holds deploy credentials, and it runs only listed fixes. Log lines count as data, never as instructions.
Evaluation
Replay 50 past incidents and check the top hypothesis against the real cause. Then use shadow mode: the agent sees real alerts but acts on none, and on-call grades it.
Another design
For alerts with one known fix, such as a full disk, a plain script is better than a model.
What could go wrong
A confident wrong answer misleads the people on call. So the agent shows its evidence and labels guesses as hypotheses.
A rollback undoes a database change. Each runbook lists its preconditions, and code checks them before offering the fix.
Long-running project agent
Your team wants an agent to build an admin dashboard from a spec with 40 features, in two-hour sessions over two weeks. The team reviews progress each morning, and the agent must pick up where it left off, even after a crash.
What carries over between sessions, and where?
What does "done" mean for one feature?
How do you stop it from declaring victory early?
Reveal one reasonable architecture
Keep progress in files and git, not in a conversation. Each session starts fresh, reads the progress notes, builds one unfinished feature, and commits when its test passes. Only a passing test marks a feature done.
Session starts and takes the repo lock (code)
|
v
ReadPROGRESS.md, features.json, git log; run smoke tests
|
v
Pick one unfinished feature (model)
|
v
Build it --> run its test --> fix --> repeat (model)
|
v
Test passes --> commit; code marks the feature passing
|
v
Budget nearly used --> write a handoff note, end (code)
Tools
edit_fileReversible write Each session starts from a clean checkout, so git can restore every file to the last commit.
run_commandDepends on command It runs only in the sandbox, for tests and the dev server.
ask_team(question)External It posts a question, and the agent moves to another feature while it waits.
Design decisions
State
The repo holds it: features.json lists each feature's test and status, PROGRESS.md holds notes and decisions, and git records every step.
Memory
A project instructions file lists the stack, the commands, and team decisions.
Subagents
A read-only reviewer checks each feature against the spec before the commit. Only one agent writes code, so edits never conflict.
Triggers
Sessions start on a schedule or on request. A lease, which is a lock that expires, stops two sessions from running at once.
Approvals
People merge in the morning review, and only people change the spec, the feature list, or the tests.
Security boundary
A container holds the repo and fake data. The tests and features.json are read-only to the agent, so it cannot change what counts as done.
Evaluation
First try a five-feature spec with a known result. Then track features passing and features that break again.
Another design
One long conversation with compaction is simpler, but each compaction can lose a decision.
What could go wrong
A new feature breaks an old one. So every passing feature's test runs before each commit.
A crash leaves half-done work. The next session discards uncommitted changes and restarts that feature.
Final scenario: migrate 300 repositories
No chapter covers this one directly. Your company is replacing its logging library across 300 repositories owned by 40 teams. The goal is a reviewed pull request in every repo within six weeks, with tests passing and no change to the log lines that alerts watch.
Which parts are deterministic, and where does a model help?
How do you avoid duplicate pull requests when a job retries?
What counts as done, for one repo and for all 300?
How do you avoid swamping 40 teams with reviews?
Reveal the rubric for this scenario
There is no single answer, so grade your design with this table, then with the general rubric below. Many strong designs use a queue with one job per repo. A codemod, which is a script that rewrites code mechanically, makes most of the changes, and an agent fixes the rest.
300 repos --> one job per repo in a queue (code)
|
v
In a fresh sandbox: the codemod rewrites the calls (code)
|
v
Build and test --> pass --> open or update the PR (code)
|
v fail, or calls the codemod skipped
Agent fixes the rest (model, at most 40 steps)
|-- pass --> open or update the PR (code)
|-- fail --> report for a person
Rubric for the migration
Area
A strong design
A weak design
Model vs code
A codemod does most of the work, and a pilot measures what is left.
An agent rewrites every repo from scratch.
Unit of work
One job per repo, with its status and pull request stored in a database.
One long conversation that "remembers" which repos are done.
Duplicates
A fixed branch name per repo, and a check for an existing pull request before opening one.
A retry opens a second pull request.
Security boundary
A fresh container per repo, because build scripts are untrusted code. The token can open pull requests but not merge them.
One machine with an admin token for every repo.
Done
Tests pass, no old imports remain, and a golden test (a comparison with saved, known-good output) shows alert-related log lines are unchanged.
"The agent said done."
Human load
About 50 pull requests a week in total, one or two per team, paced by each team's review capacity. Each description says how the change was checked.
300 pull requests on day one.
Evaluation
A pilot on 10 varied repos before the full run.
Run on all 300 and see.
Traps in this scenario
Structured log fields can record whole objects, including personal data that the old format left out.
Some repos call the logger through wrapper functions, so both the codemod and a text search miss them.
Grade your own design
Suppose a design says "a human reviews risky actions". That scores 1 on approvals, because it names neither the actions nor the reviewer. Score every row this way: 0 if it is missing, 1 if it is vague, and 2 if it is specific and says why.
Design rubric
Criterion
Scores 2
Red flag
Tradeoffs
Says which steps the model decides and why, and names one rejected alternative.
"It's an agent", with no reason.
Security boundary
Names what the process can touch at all (files, network, credentials) and what enforces each limit.
"The prompt tells it not to."
Approvals
Names each gated action, who approves, what they see, and what happens on a rejection.
"A human reviews it", or approvals for everything.
Done criteria
Lists checks that code runs, separate from the model's claim.
"When the agent says it's done."
Failure
Says what happens after a timeout, a retry, or a crash, for each external action.
Retries that can repeat an action.
Context
Says what the model sees in each call and what stays out, such as secrets.
"Give it everything."
Evaluation
Uses cases with known answers, runs several trials each, and counts policy breaks as failures.
"We'll see if users like it."
Look at the zeros first. A zero on the boundary, the approvals, or the done criteria means the design is not ready to hand to an AI coding system, even if the total is high. Chapter 21 shows how to turn these rows into tests that the finished system must pass.
Try it on the reveals above. They are not perfect, so find a row where one scores 1.