Examples Before Algorithms
Cart = [] → add Laptop → [Laptop × 1] → add Laptop → [Laptop × 2] → add Mouse → [Laptop × 2, Mouse × 1]. Four states and three operations, and the algorithm for addItem is already visible in them. If you cannot explain what should happen with a concrete example, you are probably not ready to implement 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 know the cart's operations and rules and still cannot see the algorithm. What do you write down so that the algorithm becomes visible?
You have "add item" as a name and "one entry per product" as a rule, and the function body is blank. You try to think about adding in general and your mind slides off it. You can feel that there is a find and a branch somewhere, but not in what order, or what the branch is on.
Ask an AI to write addItem, or find one in a tutorial, and read the result to see how it thinks about the problem. Reading working code seems like the fastest way to see the algorithm.
The code arrives and it does have a find and a branch — and you cannot say whether the branch is right, because you never wrote down what right looks like. The code is now the definition of the behaviour, and it was written by someone who never saw your rules.
- The code arrives and it does have a find and a branch — and you cannot say whether the branch is right, because you never wrote down what right looks like. The code is now the definition of the behaviour, and it was written by someone who never saw your rules.
- The tutorial's
addItemappends. Its cart shows Laptop twice, and nothing in your head objects, because "same product twice" was never a case you examined; the code has decided a rule you had not yet noticed you needed. - Asked to explain the function line by line, you can describe what each line does and not why it is there. That is the shape of code that was received rather than derived, and it will not survive the first change.
The move
Precisely enough to apply it to a problem you have never seen — not a slogan.
- Refuse to think about the operation in general. Pick a concrete cart, apply the operation once, and write what the cart looks like afterwards. Then apply it again with a different input. Then again with the same input as before. The sequence of states is the specification, and it was written without any code.
- Read the algorithm off the examples. Between [Laptop × 1] and [Laptop × 2] the number of entries did not change and a quantity did; between [Laptop × 2] and [Laptop × 2, Mouse × 1] an entry appeared. Two different things happened, so there is a branch; what decided it was whether the product was already there, so there is a find before the branch. The algorithm — find, branch, increase or append — was in the examples, not in your head.
- Use the moment of hesitation. If you cannot write the
afterfor somebeforeand operation, you are not ready to implement — not because you lack skill, but because a decision has not been made. Make it as a rule, write theafter, and continue. Every hesitation you resolve here is a bug you do not write later. - Keep the examples. They are the tests, once the code exists; they are the explanation when someone asks why the function has a find in it; and they are the thing you check the received code against, if you do decide to read someone else's.
The sequence that contains the algorithm
Three operations on one cart, from the concept record. The device names what changed each time, because the change is the point: the first and third calls added an entry and the second changed a quantity. The same operation did two different things, and what differed between the calls was whether the product was already present. That sentence is the algorithm.
Cart = [] → after add Laptop: [ Laptop × 1 ]
Cart = [ Laptop × 2 ]
The third example, and the branch it reveals
Add a different product to the same cart and an entry appears again, while Laptop is untouched. Now there are two examples of the same operation with different kinds of change, which means a branch, and one thing the code must look at to choose: is there already an entry with this product id? The find comes before the branch because the branch needs its answer. Nothing here was invented; it was read off the states.
Cart = [ Laptop × 2 ]
Cart = [ Laptop × 2, Mouse × 1 ]
1function addItem(cart, productId, quantity = 1):2 item = find entry in cart.items with item.productId == productId -- what differed between the calls3 if item exists:4 item.quantity = item.quantity + quantity -- add-again5 else:6 append { productId, quantity } to cart.items -- add-first, add-second7 return cartThe two rule checks — product exists, quantity positive — are not in this version yet; they arrive from the invalid examples, and go before the find because rejection must leave the state untouched.
From "how do I implement a cart?" to a question with an answer
The reflex asks the general question and gets general code. The examples ask a question that has a checkable answer, and the decomposition below shows the cart broken down by examples rather than by layers — every leaf is a before / after someone could run, which is what makes it testable.
- └A product not yet in the cart— the normal casetestable [] + add Laptop → [Laptop × 1]; [Laptop × 2] + add Mouse → [Laptop × 2, Mouse × 1].
- └A product already in the cart— the case that reveals the findtestable [Laptop × 1] + add Laptop → [Laptop × 2], and the cart still has one entry.
- ├An input the rules refuse— the case that fixes where the checks go
- └Quantity 0testable [] + add Laptop × 0 → rejected with "quantity must be positive"; cart still [].
- └Unknown producttestable [] + add Toaster → rejected with "unknown product"; cart still [].
Every leaf is an example; the examples are the tests; the tree is the plan for the function, and none of it names a language.
why The best form has one answer that can be right or wrong — [Laptop × 2], quantity changed, entries unchanged — and that answer contains the branch; the vague form can only be answered with someone else's code.
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.
- Write before → operation → after for the empty cart first, then for a cart that already contains the product, then for a cart containing something else (Example-Driven Thinking).
- Name what changed between before and after, explicitly: "Laptop's quantity: 1 → 2; entries: unchanged". The
changedlist is where the branch shows itself. - When two examples of the same operation change different things, there is a branch; ask what the code must look at to choose, and that is the lookup.
- Write the example you cannot complete as a question, decide it as a rule, and come back (Rules Come From Examples).
- Only when the sequence of states is written and each transition is explained in words, write the pseudocode — from the examples, not from memory (Plain-English Logic Is the Algorithm).
Worked on a concrete problem
The move has to produce something. This is what it produced.
- Cart = []. add Laptop → [Laptop × 1]. Changed: an entry appeared. add Laptop → [Laptop × 2]. Changed: a quantity, not the number of entries. add Mouse → [Laptop × 2, Mouse × 1]. Changed: an entry appeared, Laptop untouched. Two kinds of change from one operation means one branch, and the thing that differed between the second and third call was whether the product was already in the cart.
- From that: find the entry with this product id; if found, increase its quantity; otherwise append a new entry. The numbered plain English in the concept record is exactly this reading, with the two rule checks — product exists, quantity positive — placed before the find because the invalid examples say the state must be untouched on rejection.
- The version that appends without a find would have produced [Laptop × 1, Laptop × 1] on the second add. The example is what makes that visibly wrong; without it, the duplicate looks like a design choice.
How you know it worked
What now exists that did not before, and what question you can now ask.
- A written sequence of cart states exists for every operation, with the change between each pair named, and no code has been written yet.
- You can point to the example that produces each branch of the algorithm.
- Received code — from a tutorial, a colleague or an AI — can be checked: run the examples against it and see which after it gets wrong.
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 operation, what does the state look like before, and what exactly does it look like after?
- ?Between which two examples did something different happen — and what did the code have to look at to choose?
- ?Which example can I not complete without deciding something?
- ?If I ran these examples against the code I was about to copy, which one would fail?
What can go wrong
- Examples are written only for the empty cart, so every add looks like an append and the find never appears. The second example must reuse a product.
- Examples are written after the code, from the code, and confirm it. They then test the implementation rather than the behaviour, and a bug in the code becomes a bug in the examples.
- The examples get so numerous that the sequence of states becomes a table nobody reads. Three or four per operation — normal, edge, invalid — is what the algorithm needs; the rest are tests, and belong in the test file.
- Writing four examples before a three-line function is slower than writing the function, and on an operation you have written many times before it is theatre.
- Examples fix decisions early; "same product twice → increment" is now a commitment that a later "wishlist allows duplicates" requirement has to undo.
- A concrete example can hide a general case: [Laptop × 2] says nothing about a cart with a thousand entries, and the algorithm read off small examples may be the wrong one at scale.
- "So examples replace understanding the algorithm." They are how the algorithm is found; the reading — two kinds of change, therefore a branch, therefore a lookup — is the understanding. Examples without the reading are a table.
- "This is test-driven development." It is upstream of it. TDD writes the test first in code; this writes the state change first in words, and only some of them become tests.
- "Never read someone else's cart." Read it after your examples exist, and check it against them; that is reading code as a reviewer, which teaches, instead of as a recipient, which does not (Before You Copy Code).
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.
- GENERALBefore / operation / after is how any stateful concept becomes visible — a rate limiter's window, a queue's jobs, a login's session — and reading the branch off two differing examples is the same move everywhere.
- TEAM-SPECIFICA learner needs the examples written down; an engineer who has implemented the cart before runs them in their head and the visible step is the one or two edge cases they still write out, so the move shrinks with familiarity rather than disappearing.
- ILLUSTRATIVELaptop, Mouse and the quantities are the concept record's invented example; any real cart's products and numbers would do, and the reading would be the same.
Where the depth lives
This domain asks the question and hands the answer off by name.