DecompositionGENERALCONTESTEDILLUSTRATIVE

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.

The question

You have split the problem. How do you tell whether a piece is a real subproblem or just a heading with a confident name?

The situation

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.

The reflex

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.

Why it stalls

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.

What the reflex produces — and fails to produce
  • 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.
ProblemUnderstandRequirementsConstraintsUnknownsDecompositionSmallest StepModelExperimentObserveDebugLearnIterate

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.

Payments, decomposed until the leaves pass
Payments
  • Create a charge for an orderthe 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 confirmationthe 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 outcomethe 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.

How a piece fails to be a subproblem
TriggerSymptomCauseResponse
The piece is one word — "Payments", "Search"Nobody can say what building it means; estimates vary wildlyFails understandable: it is an unknown wearing a headingSharpen it into questions, then let the answers become children
No one can say how they would know it worksIt ends when time runs out and reopens laterFails testable: it is a heading, not a leafSplit until each leaf has a "works when" sentence
Finishing it changes nothing a person would noticeIt is done and the demo looks the sameFails meaningful: it is a fragment of something elseMerge it into the capability it serves
Starting it reveals three problems insideMonday's task becomes a week'sFails small enough: it is a subtreeRecurse: 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.

A decomposition outline with test lines
1Checkout
2 Load the cart
3 works when: the current customer's lines and quantities are returned; an empty cart is refused with a reason
4 Validate items
5 works when: a line whose product is out of stock is reported by name; all-valid carts pass
6 Calculate total
7 works when: total = sum(quantity x current price); a changed price since adding is used and shown
8 Create payment <- no test line yet: still a heading
9 Create order
10 works when: exactly one order exists with the validated lines and the captured prices
11 Confirmation
12 works when: the customer sees the order number; the cart is empty afterwards

The 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.

Next questions
  • ?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

How the move itself fails
  • 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.
What the move costs
  • 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.
Misreads
  • "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.