Task Decomposition
Split a large request into ordered, independently verifiable steps so each prompt is small enough to get right.
TL;DR
- Break a big task into small, ordered steps, each with a clear, checkable result.
- Small prompts fail less and are far easier to verify than one giant request.
- Verify each step before the next so errors do not compound down the chain.
Why Decompose
Smaller = More ReliableA focused prompt has fewer ways to go wrong than a sprawling one.
One function + its test
>>> "build the whole feature"Localizes ErrorsWhen a small step fails, you know exactly where the problem is.
Step 3 failed -> look at step 3,
not all of it.Stops CompoundingVerifying each step prevents later work from inheriting a bug.
Verify step N before step N+1.How To Split
By DeliverableEach step should produce something runnable or readable you can judge.
1 schema -> 2 validator -> 3 route
-> 4 test -> 5 wire-upBy DependencyOrder steps so each has what it needs from the ones before.
Types first, then the code that
uses the types.Isolate RiskPull the uncertain part into its own step so you can probe it early.
Prototype the tricky parser first,
alone, before integrating.Drive The Sequence
Name The Current StepTell the model exactly which step it is on so it stays scoped.
"Step 2 only: the validator.
Do not touch the route yet."Carry ForwardFeed the verified output of one step as the input to the next.
"Here is the approved schema.
Now write its validator."Keep A ChecklistTrack done vs pending so nothing is skipped or repeated.
[x] schema [x] validator
[ ] route [ ] testWhen Not To
Trivial TasksA one-function utility does not need a multi-step plan.
"Write a slugify function" -> one shot.Tight CouplingIf pieces only make sense together, group them into one coherent step.
A type and its single user
can share a step.Over-SplittingSteps too small add overhead without adding verifiability.
Do not split one function into
five prompts.Tips
- Make each subtask produce something you can run or read, not an abstract half-step.
- Keep a running checklist and tell the model which step is current so it stays scoped.
Warnings
- One mega-prompt for a whole feature usually returns code that is wrong in a way that is hard to locate.
- If you let errors ride to the next step, every later step builds on the mistake.
In Practice
A 'user profile page with editing' request, broken into steps that each yield something you can check. You verify each before moving on, so bugs stay local.
- The feature is split by deliverable, from data layer up to the UI.
- Each step produces a runnable or readable unit you can confirm.
- You name the current step so the model does not jump ahead.
- A checklist tracks progress and keeps the sequence honest.
Feature: editable user profile page.
We'll do it in steps; one at a time.
Plan:
1. Type: Profile (fields + types)
2. API: GET /profile and PUT /profile
3. Data hook: useProfile() read + update
4. UI: read-only profile view
5. UI: edit form with validation
6. Test: update flow
Start with step 1 only: define the Profile type
and its zod schema. Stop and show me before step 2.
# After approving each step:
"Approved. Step 2 only: the two routes."FAQ
Small enough that its output is independently verifiable, and no smaller. A good subtask produces something you can run, test, or read and judge correct, such as a single function with its test, rather than a vague fragment.
Either can draft it, but you approve it. Ask the model to propose a breakdown, then adjust the order and scope. You own the sequence because you know what 'done and correct' looks like at each step.
Because errors compound. If step two is subtly wrong and you push on, steps three through six inherit the bug and may paper over it. Verifying each step keeps failures local and cheap to fix.
It feels slower per step but is faster overall on non-trivial tasks, because you avoid the long debugging session that a wrong monolithic answer creates. You trade a little upfront structure for far less rework.