ExecutionGENERALSTAGE-SPECIFICILLUSTRATIVE

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 moveWorked exampleNext questions

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

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?

The situation

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.

The reflex

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.

Why it stalls

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.

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

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.

noyesnoyesyes — one entry per productnoaddItem(cart, productId, quantity = 1)Product exists in catalog?reject "unknown product"quantity > 0?reject "quantity must be positive"Already in cart? (find by productId)increase existing.quantitycreate CartItem { productId, quantity } and appendreturn cart
UserLLMAgentToolDataDecisionHumanGuardrail

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.

DiamondRule from the conceptExit or pathExample 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? — yesOne logical entry per product.increase, then returnadd-again
Already in cart? — noOne logical entry per product (the other half).create, append, then returnadd-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.

Which check runs first when both would fail?

Unknown product and a zero quantity arrive together. Which rejection does the caller see?

Catalog first (the reference's order)

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.

Quantity first

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.

Collect both and reject with all reasons

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.

concept Shopping Cart beginner
Build it step by step →

Shopping Cart = A temporary collection of products the user intends to purchase, held between browsing and checkout.

Identity, ownership, lifetime
  • 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.
State it must remember
  • 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.
Operations
  • 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
Rules that must always hold
  • 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.

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

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