Draw the Branches Before You Trust Them
addItem has two decisions and three exits, and every one of them is a rule. A flowchart of one operation is a map of its branches — which is a list of the cases the tests need and a picture of which order the checks run in.
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.
How many ways can a call to addItem end, and which check decides each one — and can you draw that without reading the code line by line?
You wrote addItem from the pseudocode and it works on the three examples. Reviewing it, a colleague asks "what if the product does not exist and the quantity is also zero — which error do they get?" You do not know, and you realise you never thought of the checks as having an order.
Read the code and answer from it. The catalog check is on line two, so "unknown product" wins; question answered. Flowcharts are for people who cannot read code, and this is ten lines.
The answer came from the code, so it is only as right as the code; the question was whether the code is right. Nothing in "line two runs first" says whether the catalog check should run first, and the concept's rules do not say either — the order is an unexamined decision.
- The answer came from the code, so it is only as right as the code; the question was whether the code is right. Nothing in "line two runs first" says whether the catalog check should run first, and the concept's rules do not say either — the order is an unexamined decision.
- Ten lines with two branches have four paths; ten lines with three branches have up to eight. The count of paths is what the tests have to cover, and reading the lines does not produce the count. Two of the paths are still untested and nobody knows which.
- When the operation grows — the maximum-quantity rule, the inventory check — the reader has to re-derive the whole branch structure from text every time. The picture would have gained a diamond.
The move
Precisely enough to apply it to a problem you have never seen — not a slogan.
- Draw the operation as boxes and decisions. Each decision is a diamond with a question that has a yes and a no; each exit is a terminal — the returned cart, or a rejection with its wording. Do not draw the syntax; draw the choices. addItem, drawn, is: Product exists? → no: reject. Quantity positive? → no: reject. Already in cart? → yes: increase; no: create. Return.
- Read the diamonds as rules. "Product exists?" is the rule "an unknown product cannot be added"; "Already in cart?" is "one logical entry per product". A diamond that does not correspond to a rule is a decision the concept did not ask for, and a rule with no diamond is unenforced (Rules Determine Implementation).
- Read the order of diamonds as a decision. Catalog before quantity means an unknown product with a zero quantity is reported as unknown; the reverse order reports it as zero. Either can be right; the flowchart makes you choose rather than inherit the order the lines happened to be typed in.
- Count the terminals. Each is a case: two rejections and two successes here, four paths, and the concept's three examples cover three of them. The fourth — an unknown product — needs an example, and the flowchart is how you found out.
addItem as decisions and exits
The chart, drawn from the plain-English steps. Three diamonds, four terminals. Each diamond has a rule written beside it in the paragraph below, and the terminals are the cases the tests need. The order of the first two diamonds is a decision the concept did not make and the chart forces.
Reading it against the concept's examples: add-first takes the bottom-left path, add-again the bottom-right, invalid-zero exits at the second diamond. No example exits at the first diamond — that is the gap.
Each diamond is a rule
The matrix pairs every decision with the rule it enforces, the rejection or path it produces, and the example that exercises it. The empty cell in the last column is the finding: the catalog rule had no example, so it had no test, so the "unknown product" wording was never asserted.
| Diamond | Rule from the concept | Exit or path | Example that covers it |
|---|---|---|---|
| Product exists? | An unknown product cannot be added. | reject "unknown product" | none in the concept — write [] + add "toaster" → rejected |
| quantity > 0? | Every quantity is greater than zero. | reject "quantity must be positive" | invalid-zero |
| Already in cart? — yes | One logical entry per product. | increase, then return | add-again |
| Already in cart? — no | One logical entry per product (the other half). | create, append, then return | add-first, add-second |
The order of the checks is a decision
Two valid charts differ only in which diamond comes first. Neither is wrong; each changes what the caller is told in the one case where both checks fail. The device asks you to choose, and to write the choice down where the next person will find it.
Unknown product and a zero quantity arrive together. Which rejection does the caller see?
when The product's existence is the more fundamental fact — a quantity of a thing that does not exist is not a meaningful complaint. Most stores want "this product is gone" to win.
cost A catalog lookup runs even for inputs that would be rejected for free by the quantity check — a cost that matters only if the catalog is a remote call.
when The catalog check is expensive (a network round trip) and the quantity check is local; reject the cheap way before paying for the expensive way.
cost The caller with a stale product and a typo in the quantity is told about the typo, fixes it, and is then told the product is gone — two round trips of frustration.
when A form that shows every error at once — common in frontend validation of the same rules.
cost Every check always runs; the rejection is a list, and the API contract has to say so. Overkill for a cart, normal for a checkout form.
The implementation ladder
Concept, examples, pseudocode, code, tests, production — for the concept this lesson is about. Code is the fourth tab, not the first.
Shopping Cart = A temporary collection of products the user intends to purchase, held between browsing and checkout.
- Does a cart have identity? Yes, weakly. Two carts with the same items are still two carts, because each belongs to someone and will become a different order. It needs an id once it leaves memory; in memory the variable is the identity.
- Who owns it? A shopper — a logged-in user or an anonymous session. The owner is part of the state because "my cart" has to be findable again.
- How long does it exist? From the first add until checkout or abandonment. Whether it survives a reload, a closed browser or a login is not a property of the concept; it is a persistence decision made later, and each answer changes where the cart lives.
- Should it survive reload? Usually yes for a store, usually no for a demo. V1 in memory says no; V2 browser storage says yes on one device; V3 server storage says yes everywhere the user is logged in.
- Should it survive login? Only if an anonymous cart and a logged-in cart are merged — a rule that does not exist in V1 and appears as a modification later.
- itemscollection of CartItemkeepThe cart is its items; without them nothing else means anything.
- items[].productIdidkeepThe reference to what is being bought. The catalog owns the product; the cart only points at it.
- items[].quantityinteger > 0keepTwo laptops is one entry with quantity 2, not two entries — the rule "one entry per product" needs a quantity to hold.
- owneruser id or session iddependsSo the cart can be found again by the person it belongs to.
- items[].productNamestringderiveIt would be convenient to render the cart without a catalog lookup.
- items[].pricemoneydependsThe total needs a price per item.
- totalmoneydropEvery screen shows the total.
- currencycodedependsPrices need a currency to be added.
- createdAttimestampdropAbandoned carts might be expired or emailed about.
- update Add item — the updated cart
- delete Remove item — the updated cart
- update Change quantity — the updated cart
- read View items — the list of entries — product id and quantity — for rendering
- domain Calculate total — the sum of price × quantity over the entries
- delete Clear cart — the empty cart
- • Every quantity is greater than zero.
- • One logical entry per product.
- • The total is never negative.
- • An unknown product cannot be added.
- • Quantity cannot exceed available stock — if inventory is enforced here.
How to do it
Most important first.
- Draw addItem from the plain-English steps, not from the code: every "check that" is a diamond, every "if one exists / otherwise" is a diamond, every "return" or "reject" is a terminal.
- Label each diamond with the rule it enforces. If you cannot, ask whether the branch belongs there (From Invariant to Validation).
- List the terminals and match each to an example. An unmatched terminal is a missing example, and therefore a missing test (Examples Become Tests).
- Draw changeQuantity the same way and notice one of its diamonds leads into another operation's chart — set to 0 → removeItem. Shared paths are where a fix in one operation changes another.
- When a rule is injected — maximum quantity — add the diamond to the drawing first and decide where it goes before touching code (Inject a Constraint and Follow It Through).
Worked on a concrete problem
The move has to produce something. This is what it produced.
- addItem, drawn: Start → "Product exists?" — no → reject "unknown product". Yes → "Quantity > 0?" — no → reject "quantity must be positive". Yes → "Already in cart?" — yes → increase the entry's quantity; no → create a CartItem and append. Both → return the cart. Two rejections, two successes, four terminals.
- Matching to examples: add-first is the "no, create" path; add-again is the "yes, increase" path; invalid-zero is the quantity rejection. The catalog rejection has no example in the concept's six — so one is written:
[], add "toaster" → [] (rejected: unknown product), and a fourth test comes from it. The chart found a gap the code's green tests did not. - The colleague's question, answered from the chart: unknown product and zero quantity hits "Product exists?" first and gets "unknown product". Then the better question: is that the right order? A store would rather tell the user the product is gone than that their quantity is wrong — so yes, and now it is a decision, written next to the chart, instead of an accident of line two.
- Injecting "maximum 5 per product": a new diamond after "Already in cart?" on the yes path — "existing + quantity > 5?" — and on the no path — "quantity > 5?". Two diamonds or one, placed after the find, because the check needs the existing quantity. The drawing settled where the rule goes before a line was typed.
How you know it worked
What now exists that did not before, and what question you can now ask.
- The chart has one diamond per rule and you can say which rule each is.
- The number of terminals equals the number of examples plus the ones you just wrote.
- The order of the diamonds is written down as a choice with a reason, not left as the order of the lines.
- When the next rule arrives, the first move is to place its diamond, and the placement is argued about before the code is.
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.
- ?How many ways can this operation end, and does each ending have an example?
- ?Which rule is each decision enforcing — and is there a decision with no rule, or a rule with no decision?
- ?In what order do the checks run, and is that order a choice I can defend?
- ?Where does the next rule's diamond go, and what does it need to have been looked up before it?
What can go wrong
- Drawing the syntax. A box per line — "declare existing", "call find" — is the code with worse formatting; the chart is decisions and exits only.
- Drawing the whole cart. One chart per operation; a diagram of six operations and their shared helpers is an architecture picture, not an execution map, and it hides the branch you were looking for.
- Treating the chart as documentation to keep in sync. It is a thinking tool made at the moment of writing or changing the operation; if it rots afterwards, that is fine — redraw it next time.
- Forgetting the exits are part of the picture. A chart whose rejections all flow into one "error" box has lost the wording, and the wording is what the caller sees and the test asserts.
- Drawing takes longer than reading for a function with one branch; the chart pays for itself at two branches and becomes necessary at four.
- A chart per operation is several pictures for one concept; most will be drawn on paper and thrown away, which is the correct fate for a thinking tool.
- The chart shows control flow and hides data — what the find returned is a diamond's input, not a box — so it is read together with the trace, not instead of it.
- "Flowcharts are for beginners." Diamonds-and-terminals is how branch coverage is reasoned about at any level; the seniors who do not draw it are counting paths in their heads, and they were taught to by drawing.
- "The chart should match the code exactly." The chart should match the rules; where the code has a branch the chart does not, the code is doing something unasked, and that is the finding.
- "Once drawn, keep it in the repo." Keeping it means maintaining it; the value was in the drawing, which surfaced a missing example and an ordering decision. Write those two things down and let the picture go.
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.
- GENERALEvery operation with a branch has a chart of decisions and exits; reading the diamonds as rules and the terminals as cases works for a cart, a login flow or a rate limiter alike.
- STAGE-SPECIFICDraw it when writing or changing an operation; do not maintain it afterwards. A team that keeps flowcharts as documentation is paying for pictures nobody reads, and the guide's tests are the durable version.
- ILLUSTRATIVEThe four terminals, the ten lines and the maximum of 5 are for the shape of the argument; the examples and rules are the concept's own.
Where the depth lives
This domain asks the question and hands the answer off by name.