Splitting the work into components the agent can build one at a time
The to-do program gets a feature: “Let me import my old list. I have a
text file with one task per line, and I don’t want to type forty add
commands.” The last lesson taught how to write success criteria a stranger
could check, and this feature has them. In this lesson we take the feature apart before any
agent sees it. We split it into components with one responsibility each,
we draw the dependencies between them and put them in order, and we write
the check for the first component before it exists. Then we run that
check and watch it fail, which is the state the agent starts from.
An agent given a whole feature makes the split itself, and it makes a different one every session. It also builds the parts together and tests them at the end, so a mistake in the first part is hidden by the parts built on it. Brilliant’s skills map for coding with AI lists decomposition, dependency analysis, and designing the verification as skills of the engineer [1], and Claude Academy’s playbook for an AI-native development process writes the intent down in a file before the code and keeps the checks running throughout [2]. Both put the split in your hands. The agent gets one component at a time, with its check.
The fixture
Section titled “The fixture”The fixture is the to-do program from Your first session with a coding
agent, some weeks later. It has grown a clear command and been split
into three modules: todo.py holds the commands, store.py reads and
writes the list, and render.py formats it. It is in the course repository
under site/examples/coding-with-agents/decomposing-the-work/fixture-repo/.
Copy that directory to a place of its own and change into it. A copy has
no git history, so the reset is to delete the copy and copy again. Working
inside a clone of the course repository also works, and there
git checkout -- . is the reset, but the agent then reads the course’s own
instruction files from the directories above the fixture. Claude Code, for
example, loads the instruction files in every directory above the one it
starts in [3]. The copy is the better default.
Run the program before you plan anything, so you know what the list looks like now.
Show the list
Section titled “Show the list”Run this, and compare what you see with the output below.
python3 todo.py list1. [ ] Buy milk 2. [x] Call the plumber 3. [x] Renew the passport 4. [ ] Water the plants 2 open, 2 done
Output verified in CI from site/examples/coding-with-agents/decomposing-the-work/list.py.
The suite passes, and nothing in it changes until you add the first test of this lesson.
Run the tests
Section titled “Run the tests”Run the tests and keep only the last line of the output.
python3 -m unittest -q 2>&1 | tail -1OK
Output verified in CI from site/examples/coding-with-agents/decomposing-the-work/tests.py.
The examples in this lesson aren’t graded. Each one is a command you run in the fixture, with the output the course itself checks on every build. The checkpoints ask about the split and the check, because making those is what this lesson teaches.
One responsibility per component
Section titled “One responsibility per component”The success criteria for the import feature: todo import tasks.txt reads
a text file with one task per line. Spaces before and after a line are
dropped, a line that starts with x is a done item, and blank lines are
skipped. A task already in the list isn’t added twice, and the command
prints how many items it imported and how many it skipped. add, list,
done and clear keep working.
The request names one feature, and the work in it is three components. A component has one responsibility, an interface you can state in a sentence, and a size that fits one agent session and one change you can review in a few minutes. Cut the feature where the data changes form: text lines become items, items are merged into the list, and the list is shown to the user.
| Component | Responsibility | Interface |
|---|---|---|
| Parser | Turns text lines into items | importer.parse_lines(lines) returns a list of items, each {"text": ..., "done": ...} |
| Merge | Adds new items to the list and leaves duplicates out | importer.merge(items, new_items) appends the items whose text isn’t in items and returns the count added |
| Command | Wires todo import <file> to the two above and reports | todo.py reads the file, calls the parser and the merge, prints imported N items, skipped M |
Each row answers three questions with one sentence each. If a row needs
two sentences for its responsibility, it is two components. If the interface
sentence has to describe the code inside, the boundary is in the wrong
place. Trailing spaces and the x mark are the parser’s
problem and nobody else’s, and “already in the list” is the merge’s
problem, so the command has nothing to decide.
Write the list before the brief, and put it in the brief. The agent builds the parser to the interface you named, and the brief for the merge names the same interface, whether the same agent writes it or not.
Which of these are components with one responsibility?
Section titled “Which of these are components with one responsibility?”A coding agent is going to add an import command to a small command-line to-do program. The feature reads a text file with one task per line, drops the spaces around a line, skips blank lines, treats a leading x as a done mark, leaves out tasks already in the list, and prints how many it imported and skipped. The learner is splitting it into components.
Which of these are components with one responsibility each?
Can you state what the component does, and what it takes and returns, in one sentence each?
Whose problem is it?
Section titled “Whose problem is it?”A coding agent is going to add an import command to a small command-line to-do program in three components: a parser that turns text lines into items, a merge that adds new items to the list and leaves duplicates out, and the command that reads the file, calls both and prints a report.
Which component sees this detail first, and which one never needs to know it?
Order the work by dependency
Section titled “Order the work by dependency”Three components, and they can’t be built in any order. The table fixes the item format, so the merge could be written against it today. Its check couldn’t. The merge’s test has to run on items the real parser produced, or it proves the merge works on items you typed by hand and says nothing about the imported ones. So the dependency runs from a component to the checked output of the one before it. Draw the arrows. The parser has no dependency. The merge depends on a checked parser, and the command depends on both. That order is the plan, and each step is checked before the next one starts.
The order also names the assumption everything rests on: the item format the parser produces. The parser goes first and is checked alone with its own test, and only then is the merge briefed. A component that passes its own check is a fixed point the next brief can name.
The dependency drawing also shows which components have no arrow between
them. If the feature had a fourth component, a render change that shows
how many items came from an import, its arrow would point at the command
and not at the parser. A second agent session could build it once the
command was checked, while the merge was still in progress. Naming the
dependencies is what tells you what can run side by side and what has to
wait.
Put the steps in dependency order
Section titled “Put the steps in dependency order”A coding agent is adding an import command to a small command-line to-do program in three components: a parser that turns text lines into items, a merge that adds new items to the list and leaves duplicates out, and the command that reads the file, calls both and prints a report. Each component is checked before the next is briefed.
- Brief the parser and check that its test passes
- Brief the merge and check its test on items the parser produced
- Brief the command that reads the file and calls both
- Run the whole import against a copy of the list
Which component's test needs the output of another component to run on?
Which component is built first?
Section titled “Which component is built first?”A coding agent is adding an import command to a small command-line to-do program in three components: a parser that turns text lines into items, a merge that adds new items to the list and leaves duplicates out, and the command that reads the file, calls both and prints a report.
Which component do you brief first, and why?
Which component's test can run on nothing but its own input, and which tests need another component's output?
Design the check before the code
Section titled “Design the check before the code”The parser doesn’t exist yet, and this is the moment to decide how you
know it is right. Write the acceptance test now, put it in the suite,
and name it in the brief as the command that means done. The agent then
has a target it can run itself, and “done” means the line reads OK
instead of the agent saying so.
The test has to do one more thing: it has to fail on a stub. A parser that
returns an empty list, or that returns every line as an open item, is the
kind of first draft an agent writes when the target is vague. One input
with a blank line, a done mark and a line with spaces around it is enough
to reject each of those. Create test_importer.py with this content.
import unittest
import importer
class ImporterTests(unittest.TestCase): def test_parse_lines_keeps_text_and_done_mark_and_skips_blank_lines(self): lines = ["Buy milk", "", "x Call the plumber", " Water the plants "] self.assertEqual( importer.parse_lines(lines), [ {"text": "Buy milk", "done": False}, {"text": "Call the plumber", "done": True}, {"text": "Water the plants", "done": False}, ], )Run the suite. importer.py doesn’t exist, so the test can’t even import
it, and the suite reports an error.
Run the suite before the parser exists
Section titled “Run the suite before the parser exists”With test_importer.py in place, run the tests and keep the last line. The
course checks this against its own copy of the test, which is the one
shown above.
python3 -m unittest -q 2>&1 | tail -1FAILED (errors=1)
Output verified in CI from site/examples/coding-with-agents/decomposing-the-work/failing_test.py.
Now prove the test does its job. Create importer.py with a stub that
has the right name and does nothing, and run the suite again.
def parse_lines(lines): return []Run the suite against a stub
Section titled “Run the suite against a stub”With the stub in place, run the tests and keep the last line. The course checks this against its own copy of the stub.
python3 -m unittest -q 2>&1 | tail -1FAILED (failures=1)
Output verified in CI from site/examples/coding-with-agents/decomposing-the-work/stub_test.py.
An error became a failure: the module imports, and the assertion rejects
what it returns. Delete importer.py again. The agent writes the real one. Your
brief for the first component now fits in a few lines: the parser’s
responsibility and interface from the table, the file it may create, and
the command that means done.
Add importer.py with parse_lines(lines), which turns text lines intoitems as test_importer.py describes. Create only importer.py. Done when`python3 -m unittest -q` prints OK. Do not change the test.An agent that can’t make the test pass may change the test until it does, and then the check doesn’t check anything. The last line of the brief closes that door.
Which test would fail on a stub?
Section titled “Which test would fail on a stub?”Before a coding agent builds a parser that turns text lines into to-do items, the learner writes an acceptance test for it. The test should reject a stub, a version of the parser that has the right function name and does no real work.
Which of these tests would fail on a stub parser?
Imagine a parser that returns an empty list, and one that returns every line as an open item. Which test rejects both?
What does the brief for one component name?
Section titled “What does the brief for one component name?”A learner has split an import feature for a small to-do program into components and written an acceptance test for the first one, a parser, before it exists. They are briefing a coding agent to build that one component.
Which of these belong in the brief for the parser?
What lets the agent check its own work without you, and what stops it from moving the target?
Exercise
Reset the fixture and take the import feature apart yourself, without
looking at the table above. Write the components as a list with one
responsibility and one interface sentence each, draw the dependency
arrows between them, and put them in build order. Then write the failing
acceptance test for the first component, run
python3 -m unittest -q, and check that the last line starts with
FAILED. Add a stub with the right name and run it again, and the line
must still start with FAILED. Fifteen lines of notes and one test file is
enough. You do this so that the agent builds one checked component at a
time and never gets to choose the split.
A good result: each component’s interface fits in a sentence, the first
one in your order is the one the others take input from, the suite says
FAILED before the component exists and again on the stub, and the brief
names the test run as what done means. Reflect: which of your components
would you have merged if the agent had been given the whole feature, and
what mistake could then have hidden inside it?
Stretch: Brief the agent for the parser with the brief above, in a fresh session, and read the diff: it should touch `importer.py` and nothing else.
Recap
- A component has one responsibility, an interface that fits in a sentence, and a size that fits one agent session and one reviewable change. Cut where the data changes form.
- Dependencies set the build order. The component whose output the others take is built and checked first, so a mistake in it never reaches the next one unseen.
- A component checked alone is a fixed point for the next brief. Feed the next component’s test what the first one produces, or the second inherits and hides the first one’s mistakes.
- Write the acceptance test before the component exists, put it in the suite and name the test run in the brief as what done means.
- A check that passes on a stub doesn’t check the work. Run it against a version with the right name and no work, and the line must still say it failed.
You can now
- Decomposes a problem into components with clear dependencies
- Designs how the work will be verified before it is built
References
Section titled “References”- Brilliant. Specification and design. Brilliant, Coding with AI skills map. Reference.
Brilliant SPC - Anthropic. The AI-native SDLC playbook. Claude Academy. Course.
Academy ai-native-sdlc-playbook - Anthropic. How Claude remembers your project. Claude Code documentation. Reference.
Claude Code memory