Where the Primitives Start Is Yours
The walk down goes Unknown → Unknown → Known Primitive, and where "Known" begins is different for every learner. Find your boundary, build upward from it, and stop treating someone else's boundary as the depth you owe the problem.
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.
Two people implement the same cart: one stops at "call find", the other has to write the loop, a third has to learn what an array is. Which one is doing it right — and how do you find where your own walk should stop?
A colleague sketches addItem on a whiteboard in four lines and says "it is just a find and a push". You nod. Alone at your desk, "just a find" is the part you cannot do, and you wonder whether you are supposed to go learn arrays properly before touching the cart, or whether needing to means you should not be doing this.
Adopt their depth. If the senior stops at "find", then "find" is the primitive, and you should be able to write it — so you search until you can paste something that looks like it, and the gap between their boundary and yours gets filled with syntax you cannot yet explain.
Their ladder has rungs missing for you. "Find" is one rung for them because they have written the loop a hundred times; for you it is two rungs, and pasting over the missing one leaves addItem standing on something you cannot see.
- Their ladder has rungs missing for you. "Find" is one rung for them because they have written the loop a hundred times; for you it is two rungs, and pasting over the missing one leaves addItem standing on something you cannot see.
- The opposite over-correction: deciding your boundary is "nothing", so the cart must wait until arrays, iteration, callbacks and functions are studied in full. The cart gets no closer, and the study has no problem pulling it, so it does not stick either.
- Nobody says where the boundary is, so it feels like a verdict on you instead of a fact about today. The feeling produces avoidance, not code.
The move
Precisely enough to apply it to a problem you have never seen — not a slogan.
- The walk always has the same shape — Unknown → Unknown → Known Primitive — and the same stopping rule: the first rung you could write without looking anything up. That rung is your boundary. It is not a ranking; it is a location, and it moves upward every time you build something.
- Find it by trying, not by introspecting. Take the stuck rung and give it thirty seconds. If code appears, the boundary is here; if a smaller question appears instead ("what is an entry, again?"), the boundary is lower and you have just found the next rung.
- Build upward from wherever it is. The person who stops at the loop builds findItem then addItem; the person who stops at "find" builds addItem directly; both cart implementations are the same at the top. The depth of the walk is the distance between the problem and you, and the cart does not know how deep it was.
- When the boundary is below the problem's primitives — you cannot write a loop at all — that is a foundation gap, not a deeper rung. The move changes: learn the one primitive, with the cart as the reason, then return (When the Rung Below Is a Foundation).
Three walks, one cart
The matrix shows the same operation walked by three people. The columns that differ are the depth and the number of functions written; the column that does not differ is the result. Reading it, the question "which one did it right?" dissolves — each stopped at the first thing they could write, which is the only rule.
| Learner | Stops at | Writes by hand | Looks up | Result |
|---|---|---|---|---|
| Experienced | the language's find | addItem | nothing | [Laptop × 1] + add Laptop → [Laptop × 2] |
| Has written loops | loop and compare | findItem, then addItem | how to return from inside a loop | the same |
| New to arrays | below the cart's primitives | a loop over an array first, with findItem as the goal, then the rest | arrays and iteration — only those | the same, one detour later |
Finding the boundary by writing, not by guessing
Two ways to locate the boundary. One asks yourself and gets an answer shaped by confidence; the other tries to write the rung and gets an answer shaped by what actually appears. The second is uncomfortable for thirty seconds and correct.
"I know arrays, so find is fine." Paste `cart.items.find((i) => i.productId === productId)`. It runs. When asked what the arrow function receives, the answer is a guess.
Try to write "find the entry" with no lookup for thirty seconds. Either a loop appears — boundary found, write it — or the question "how do I stop the loop early?" appears — boundary is one rung lower, and it has a name now.
Self-assessment measures familiarity with the word; attempting measures whether the piece can be produced. Only the second predicts whether the next operation can be written unaided, which is what the boundary is for.
What to do at each kind of stuck
Not every stuck rung wants the same move. The decision below is the one the button cannot make for you: is this rung too big, is it below the problem's primitives, or is it a word you have never met?
What kind of stuck is this?
when "Find the entry" is made of a loop and a comparison, and at least one of those is familiar.
cost One more press of the button; a function of your own for the rung; a few extra lines to collapse later.
when The loop itself is unknown; the cart's primitive chain ends on something you have never written.
cost A detour to one foundation — arrays and iteration — with findItem as the goal. Longer than a press, shorter than a course.
when The reference uses a construct you cannot read, but the rung underneath it (the loop) you could write.
cost Write the long form you know, then read the construct as a compression of it. Almost free, and it makes the built-in yours.
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.
- Before starting an operation, write down where you expect to stop: "I think I can write the loop but not
find". Then test the guess by writing; a wrong guess is the most useful outcome (Predict the State Before Running the Code). - When a senior's sketch skips a rung you need, ask for the rung, not the code: "what is find made of?" — the question produces a ladder in their head that they had compressed.
- Keep the rungs above your boundary as your own functions until the operation works; then, and only then, collapse them into the language's built-ins.
- Record the boundary once per concept — a line in your notes: "cart: stopped at the loop". When the next concept stops one rung higher, you can see it move (The Engineering Notebook).
Worked on a concrete problem
The move has to produce something. This is what it produced.
- Three people, one cart. Experienced: "find, then branch" — writes
cart.items.find(...)and is done in a minute; boundary at the language's built-ins. Learner who has written loops: writes findItem by hand, then addItem; boundary at loop-and-compare. Learner new to arrays: the loop itself is unknown; the boundary is below the cart's primitives, so the move becomes learning "loop over an array" with findItem as the goal, then returning. - Testing the guess: the second learner assumed they could write the loop and could not remember how to return the matched entry from inside it. Thirty seconds produced the question "how do I stop the loop early?" — a rung they had not seen, one below where they expected. They looked up only that, wrote findItem, and climbed.
- Each of the three ends with an addItem that turns
[Laptop × 1] + add Laptopinto[Laptop × 2]. The reference, read afterwards, is identical for all three; what differs is how many lines each had to write to be able to read it.
How you know it worked
What now exists that did not before, and what question you can now ask.
- You can say, in one sentence, where you stopped and why: "I stopped at the loop because I could write it and I could not write find".
- The senior's compressed sketch reads as a ladder with rungs hidden, not as a standard you missed.
- On the next concept the boundary is one rung higher, and you noticed.
- A rung below the problem's own primitives was recognised as a foundation gap and routed, not recursed into.
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.
- ?Where do I expect this walk to stop — and does thirty seconds of writing agree?
- ?Is the rung I am stuck on too big, or is it below the primitives this problem needs?
- ?What did the expert's sketch compress, and which of those hidden rungs is mine to write?
- ?Compared with the last concept, did my boundary move?
What can go wrong
- Setting the boundary by pride. Declaring "I can write find" and pasting it is the reflex wearing a badge; the boundary is found by writing, not by claiming.
- Setting it by fear. Going to arrays-from-scratch when you could already write the loop makes the walk longer than the problem and delays the only thing that moves the boundary: building.
- Treating the boundary as fixed. It moves up with every operation; a learner still writing findItem by hand on the fifth concept has stopped letting the built-ins in.
- Applying it to a team as if it were personal. On a team the codebase's conventions set a floor — everyone writes
find— and your private ladder is scaffolding to reach that floor, not a style to commit.
- A personal boundary means a personal amount of work; a learner near the bottom writes more scaffolding than a learner near the top for the same cart.
- Keeping hand-written rungs until the operation works produces code that a team would refactor; the refactor is a cost paid for understanding.
- Testing the boundary by writing costs the thirty seconds every time, even when the guess was right.
- "So there is no right level of abstraction to work at." There is — for the codebase. The boundary is about where *you* start; the code that ships stops at the team's built-ins regardless of how you got there.
- "A lower boundary means I am behind." It means today's cart is two rungs taller. The same person on the next concept has a higher boundary; the metric that matters is movement, not position.
- "Experts don't have a boundary." They have one at a different place and cross it on unfamiliar problems just as visibly — watch a senior meet a new concurrency primitive.
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.
- TEAM-SPECIFICThe boundary is defined by the person doing the walk. A solo learner stops at loops; a senior on a team stops at whole operations and the codebase's conventions; a team under deadline pins the boundary at the built-ins and treats anything below as reading, not writing. The move — find it by writing, build up from it — is the same in every case.
- STAGE-SPECIFICOn a greenfield learning project the hand-written rungs can stay; in an existing codebase they are scaffolding that gets collapsed into the project's idioms before the operation is committed.
- ILLUSTRATIVEThe three learners, the whiteboard and the thirty seconds are invented to show where a boundary sits; Laptop × 2 is the concept's own example.
Where the depth lives
This domain asks the question and hands the answer off by name.
- — The manifesto's "understanding is not delegable" applies to the boundary too: a senior can show you their ladder, but only your own attempt tells you where yours stops — see /manifesto/delegating.