What Makes a Good Subproblem
A useful subproblem is understandable, testable, meaningful and small enough to build without further splitting. Most bad decompositions fail exactly one of the four, and naming which one tells you how to fix it.
The situation, the reflex, and why it stalls
Every lesson starts where being stuck starts: someone has a problem, and the first move that comes to mind feels like progress.
You have split the problem. How do you tell whether a piece is a real subproblem or just a heading with a confident name?
Your tree for the store has children like "Payments", "Handle errors" and "Performance". They looked fine when you wrote them. Now you are trying to pick one to build, and none of them tells you what to do on Monday morning or how you would know on Friday that it is done.
Estimate them. Put a size on each child — small, medium, large — and start with a medium one. Sizing feels like the thing that turns a list into a plan, and it produces a number that can go in a spreadsheet.
The sizes are guesses about pieces that were never defined. "Payments: large" — large compared to what? It contains a provider integration, a confirmation handler, a refund path and an idempotency problem, and the estimate covers whichever subset the estimator imagined.
- The sizes are guesses about pieces that were never defined. "Payments: large" — large compared to what? It contains a provider integration, a confirmation handler, a refund path and an idempotency problem, and the estimate covers whichever subset the estimator imagined.
- A piece that cannot be tested cannot be finished; it can only be stopped. "Handle errors" ends when the sprint does, and reopens the moment an error arrives that nobody handled.
- The plan looks complete because every child has a size, while the actual next step — the first line of code for the first piece — is no clearer than before.
The move
Precisely enough to apply it to a problem you have never seen — not a slogan.
- Hold every candidate piece against four questions. *Understandable*: can you say in one sentence what it does, with no "etc."? *Testable*: can you name an observation that would show it works? *Meaningful*: does finishing it change what the system can do, in a way someone would notice? *Small enough*: could you build it without splitting it again?
- A piece that fails *understandable* is a vague word — sharpen it as you would an unknown (Unknown to Specific Question). One that fails *testable* is a heading — split it until the leaves have observations. One that fails *meaningful* is a fragment — merge it with what it serves. One that fails *small enough* is a subtree — recurse (Recursive Decomposition).
- The four are not equal. Testable is the one that cannot be faked: a piece with a named observation has been understood well enough to build, and the observation is the definition of done. When in doubt, write the test sentence first and let it drag the other three along.
A tree where every leaf passes
Here is the Payments heading after the four questions have done their work. Each leaf is a sentence, has an observation, changes what the store can do, and is buildable without another split. The parent that used to be one word is now three things someone could start on.
- └Create a charge for an order— the request side of the conversation with the providertestable For a pending order, the provider returns a charge id in test mode and it is stored against the order.
- └Receive the provider's confirmation— the response side, which arrives on its own scheduletestable The order becomes paid exactly once, even if the confirmation is delivered twice or out of order.
- ├Show the customer the outcome— the customer must not be left on a spinner
- └Successtestable A confirmation page shows the order number and the amount charged.
- └Declinetestable The order stays unpaid, the customer sees a reason they can act on, and can retry without a second order being created.
"Refunds" was proposed as a fourth child and deferred: it is testable and understandable, but not meaningful for V1, and the deferral is written down.
Each failure has its own repair
The four criteria are useful because each failure prescribes a different action. The table is the diagnosis; the "response" column is why it is worth diagnosing rather than just re-sizing the piece.
| Trigger | Symptom | Cause | Response |
|---|---|---|---|
| The piece is one word — "Payments", "Search" | Nobody can say what building it means; estimates vary wildly | Fails understandable: it is an unknown wearing a heading | Sharpen it into questions, then let the answers become children |
| No one can say how they would know it works | It ends when time runs out and reopens later | Fails testable: it is a heading, not a leaf | Split until each leaf has a "works when" sentence |
| Finishing it changes nothing a person would notice | It is done and the demo looks the same | Fails meaningful: it is a fragment of something else | Merge it into the capability it serves |
| Starting it reveals three problems inside | Monday's task becomes a week's | Fails small enough: it is a subtree | Recurse: decompose it, then pick a leaf |
The "works when" line, as a habit
A tree kept as text with one test line per leaf is enough to run the four checks by eye. The outline below is the store's checkout branch in that form; the point to notice is that any line without a "works when" stands out immediately.
1Checkout2 Load the cart3 works when: the current customer's lines and quantities are returned; an empty cart is refused with a reason4 Validate items5 works when: a line whose product is out of stock is reported by name; all-valid carts pass6 Calculate total7 works when: total = sum(quantity x current price); a changed price since adding is used and shown8 Create payment <- no test line yet: still a heading9 Create order10 works when: exactly one order exists with the validated lines and the captured prices11 Confirmation12 works when: the customer sees the order number; the cart is empty afterwardsThe line with no test is the one to decompose next. Everything else can be started.
How to do it
Most important first.
- For each leaf, write "works when: …" as one sentence. If it needs "and" three times, it is several leaves. If it cannot be written, it is a heading.
- Read the leaf's name aloud to a non-engineer. If they cannot picture what will exist, it fails meaningful — either sharpen it or fold it into a parent they can picture.
- Ask "could I start this Monday and finish it without discovering it is really several things?" If not, split; if the answer is "it is really a tiny part of something else", merge.
- Prefer leaves whose observation is a customer- or admin-visible fact. Internal observations ("the function returns the right value") are fine for helpers, but the tree should mostly bottom out in things someone would see (Invariants as Tests).
Worked on a concrete problem
The move has to produce something. This is what it produced.
- "Payments" against the four: understandable — no, it is one word; testable — no observation; meaningful — yes; small — no. Split: "create a test-mode charge for an order" (works when: the provider returns a charge id and it is stored on the order), "receive the provider's confirmation" (works when: the order becomes paid, once, even if the confirmation arrives twice), "show the customer the result" (works when: success shows a confirmation with the order number, failure shows a reason and a retry). Three leaves, each passes all four.
- "Handle errors" against the four: fails understandable and testable, and "meaningful" is unanswerable because the errors are unnamed. It is not a subproblem at all; it is a reminder to run Failure Modeling on each capability, which yields leaves like "a declined payment leaves the order unpaid and tells the customer" — testable, and living under Checkout rather than under a generic heading.
- "Show the product's price on the product page" against the four: understandable, testable, small — but meaningful only as part of "view one product", which is where it belongs. It is a fragment; merged.
How you know it worked
What now exists that did not before, and what question you can now ask.
- Every leaf has a "works when" sentence, and the sentences are short.
- You can point at any leaf and say which of the four it once failed and what you did about it.
- The pieces you doubt are marked as doubts — "not sure this is one thing" — rather than sized and scheduled.
The questions you can now ask
The field this whole domain exists for. After this lesson, these are the questions to put to an unfamiliar problem.
- ?For this piece, what would I look at to say it works?
- ?Which of understandable, testable, meaningful and small does this piece fail — and what is the repair for that one?
- ?Is this leaf a fact someone would see, or an internal detail that belongs under something they would?
What can go wrong
- Applying the four so strictly that leaves become trivial — "the button has the right label" — and the tree has hundreds of them. Meaningful is the brake: a leaf must change what the system can do.
- Confusing testable with "has a unit test". A leaf can be testable by observation — a page shows the right thing — long before any automated test exists. The sentence is what matters; automation comes when the leaf is built.
- Treating the four as a checklist to pass rather than a diagnosis to act on. The point is not the score; it is that each failure names a different repair.
- Writing a "works when" sentence for every leaf is real work, and on a problem you have solved before it is work you would have done in your head.
- Leaves that are meaningful to a stakeholder are sometimes larger than leaves that are convenient to an engineer; the four criteria pull toward the stakeholder and that costs some engineering tidiness.
- "Small enough means small." Small enough means buildable without another split — a leaf can be a few days of work and still be one thing. Small for its own sake produces fragments.
- "Testable means I need the test framework set up first." No — testable means the observation is named. The sentence "reloading shows the same cart" is a test whether or not anything automates it yet.
Where this applies
Problem-solving advice is stated as universal far more often than it is. These labels say what each method is specific to — and where CONTESTED appears, the note gives the strongest form of the opposing view.
- GENERALThe four criteria apply to any decomposition of any problem; what a "meaningful" observation looks like changes — visible to a customer for a store, a measurable output for a pipeline, a passing property for a library.
- CONTESTEDSome practitioners hold that a subproblem is defined by what it *needs* — its inputs and dependencies — rather than by what it *shows*, and that leading with testability biases the tree toward the visible and away from the essential machinery (a reservation model, a ledger) that nothing visible depends on yet. Their strongest point: the most important early leaf is sometimes one nobody can see working, and a testability-first tree postpones it. The reply here is that even that machinery has an observation, just an internal one, and naming it is still the test.
- ILLUSTRATIVEThe three leaves under Payments and the fragments that were merged are invented for the example; a real provider integration would decompose according to that provider's actual flow.
Where the depth lives
This domain asks the question and hands the answer off by name.