ConceptGENERALSTAGE-SPECIFICILLUSTRATIVE

Who Owns It, and How Long Does It Exist?

A cart belongs to a shopper and lives from the first add until checkout or abandonment. Whether it survives a reload, a closed browser or a login is not part of the meaning — each is a question whose answer is a persistence decision made later. The definition's job is to ask the questions, name the default, and say which version answers each.

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

Who does the cart belong to, and when does it stop existing — and which of those answers are part of the concept, and which are decisions you get to defer?

The situation

You have the sentence and the identity answer. Someone asks whether the cart should still be there tomorrow. You say "of course", and then someone else asks whether it should still be there after logging in on a different phone, and you realise you are about to design a database to answer a question about a concept.

The reflex

Decide persistence now. Pick a database, add ownerId and createdAt and expiresAt to the cart, and write the schema — because the questions are about storage and storage means a schema.

Why it stalls

The schema exists and the cart does not. Three fields were added for questions no V0 operation reads; createdAt waits for an expiry job nobody has scheduled, and the first cart cannot be tested without a database running.

What the reflex produces — and fails to produce
  • The schema exists and the cart does not. Three fields were added for questions no V0 operation reads; createdAt waits for an expiry job nobody has scheduled, and the first cart cannot be tested without a database running.
  • The questions got answered by the storage choice instead of by the concept. "Survives login?" became "whatever happens when the session cookie changes" — an accident of the implementation, discovered by a shopper who lost a cart.
  • The answers cannot be revisited, because they are columns. When the business decides an anonymous cart should merge into the user's cart, the schema has one owner per cart and the merge is a migration rather than a rule.
ProblemUnderstandRequirementsConstraintsUnknownsDecompositionSmallest StepModelExperimentObserveDebugLearnIterate

The move

Precisely enough to apply it to a problem you have never seen — not a slogan.

  • Separate two questions the reflex fuses. Owner: who is this thing *for*, and how will they find it again? Lifetime: when does it begin and when does it end? Both are part of the concept — a cart with no owner cannot be "my cart", and a cart with no end is a wishlist. The record answers: a shopper, logged-in or anonymous; from the first add until checkout or abandonment.
  • Then list the survival questions and refuse to answer them as part of the concept. Survive a reload? A closed browser? A login? The record says: "not a property of the concept; it is a persistence decision made later, and each answer changes where the cart lives." Write each as a question with the version that answers it.
  • For each survival question, name the default that V0 takes and what would make the answer change. V0 in memory: nothing survives, and that is fine for a demo and wrong for a store. V2 browser storage: survives a reload on one device. V3 server storage: survives everywhere the user is logged in. The concept is the same at every level; the answer to "how long" is a level, not a field.
  • Keep the owner in the concept but out of V0's state, with a note. "In a single in-memory demo there is one cart and no owner" — the record marks owner as depends, with the trigger: "the moment there are two shoppers or any persistence, it is required." That is an unknown turned into a specific question with an experiment: add a second shopper and see what breaks (Unknown to Specific Question).

Owner and lifetime as an unknowns board

What the concept settles, what it deliberately leaves open, and what V0 assumes. Each open question has the experiment that would answer it — and the experiment is usually "build the version that needs the answer and see what breaks".

The cart's owner and lifetime
known
  • A cart belongs to a shopper — logged-in or anonymous — because "my cart" has to be findable again.
  • A cart begins at the first add and ends at checkout or abandonment; "temporary" is in the sentence.
  • Checkout is the only end V0 implements, as clear; abandonment has no operation yet.
assumed
  • ~V0: one shopper, one process, nothing survives — written down so that V1 and V2 know exactly which assumption they break.
unknown → question → experiment
  1. ? It should probably still be there tomorrow.

    becomes Must the cart survive a page reload on the same device, and if so where is it stored between reloads?

    experiment Build V2: serialise on every change, load on start, and add a product to storage that the catalog no longer has — the stale-product failure is the cost of "yes".

  2. ? What about their phone?

    becomes Must the cart be the same on every device where the shopper is logged in?

    experiment Build V3: a cart row and cart_item rows with an owner; open two tabs and add the same product — the race is the cost of "yes".

  3. ? Login is weird.

    becomes When an anonymous shopper logs in, does their anonymous cart merge into the account's cart, replace it, or disappear — and what happens to a product in both?

    experiment Write the before/after for [Laptop × 1] anonymous + [Laptop × 2] account and pick an after; the pick is V4's merge rule.

Lifetime, level by level

The record's persistence ladder, read as answers to "how long does it exist?". The concept never changes; each level answers one survival question and pays for it. That the answers are levels rather than fields is the point of deferring them.

Where "how long" gets answered
  1. In memory (V0–V1)
    The cart lives as long as the process; a reload empties it.A demo or a test needs nothing more, and every rule can be checked without a store running.
  2. Browser storage (V2)
    Serialised on every change, loaded on start; survives a reload on one device, invisible to the server and to other devices.A reload emptying the cart is the first thing a real shopper notices — and this is the cheapest "yes".
  3. Server storage (V3)
    A cart row with an owner; the same cart from every device where the shopper is logged in; two tabs now race.The cart must be trusted for checkout and found from anywhere; the owner becomes a column.
  4. Merged at login (V4)
    An anonymous cart folded into the account's cart with a rule for overlapping products.Shoppers start anonymous and log in at checkout; "one owner for the cart's whole life" is the assumption this level breaks.
  5. Expired when abandoned (V6)
    A createdAt and a job that removes or emails about old carts.Only when the business asks about abandoned carts does the second end of the lifetime get an operation — and a field.

What each deferred answer costs when it arrives

Deferral is honest only if the arrival is planned. The table lists what breaks at each level — the record's failure modes, arranged by the survival question that introduces them.

The bill for each "yes"
TriggerSymptomCauseResponse
Survive reload (V2) — a stored cart loadsA product in the cart that the catalog no longer has; view works, total throwsStorage outlives the catalog's state; the cart points at a product that is goneDecide at load: drop the item, show it as unavailable, or block checkout — and write the rule.
Survive across devices (V3) — two tabs add the same productA duplicated entry, or one add lostThe find-before-append cannot see the other tab's appendUNIQUE (cart_id, product_id) plus a retry-as-increment on violation.
Survive login (V4) — anonymous and account carts overlapThe shopper's laptop count is wrong after loginNo merge rule; whichever cart loaded last wonWrite the overlap example, choose sum / max / account-wins, encode it as a merge operation with a test.

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.

  • Write owner and lifetime as two sentences beside the concept: "belongs to…", "exists from… until…". If either is blank, the concept is not defined yet (Define the Concept).
  • Turn each survival question into a row: the question, V0's default, the version that changes it, and what that version adds (The Persistence Ladder).
  • Mark every field the survival questions tempt you to add — ownerId, createdAt, expiresAt — with the operation that would read it. No reader in this version means no field in this version (Challenging Unnecessary State).
  • Ask what "ends" means operationally: checkout consumes the cart; abandonment is a lifetime with no operation until an expiry job exists. Say which end V0 implements (only checkout, by clearing).
  • When a survival answer changes, expect a new rule, not just a new store: surviving login means a merge rule for overlapping products (When Assumptions Change).

Worked on a concrete problem

The move has to produce something. This is what it produced.

  • Owner. V0: one cart, no owner, the variable is "my cart". V1: several carts, so "which cart?" needs an answer, and the owner — a user id or a session id — becomes state. The field was in the concept from the start ("a shopper's cart") and enters the code when the first operation needs to find a cart by its owner.
  • Lifetime. Begins at the first add — an empty cart with no adds is not stored anywhere in V0, it is createCart(). Ends at checkout, which V0 implements as clear; abandonment has no operation in V0 and becomes an expiry with createdAt in V6, when the business asks about abandoned carts. Until then the field is a note.
  • Survival. Reload: V2 serialises to browser storage and loads on start — and introduces the stale-product failure, a product in storage that the catalog no longer has. Login: V4 merges an anonymous cart into the user's cart, which needs a merge rule for overlapping products — a rule that did not exist while the cart had one owner for its whole life.

How you know it worked

What now exists that did not before, and what question you can now ask.

  • Owner and lifetime are written beside the concept as sentences; the survival questions are written as a table with a version per row.
  • V0's state has no field the survival questions added, and each deferred field has a trigger that would add it.
  • When someone asks "does it survive X?", the answer is a version number and what that version breaks, not a shrug or a schema.

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
  • ?Who is this thing for, and how will they find it again?
  • ?When does it begin, and what ends it — and which of those ends has an operation in this version?
  • ?Which survival questions am I deferring, what is V0's default for each, and which version changes it?
  • ?Which field am I tempted to add now that no operation in this version reads?

What can go wrong

How the move itself fails
  • Owner is deferred along with survival, and V1 arrives with two shoppers sharing one cart because "which cart?" was never a question in the code.
  • Every survival question is answered "yes" up front, and the V0 cart needs a server, a database and a login before it can add a laptop.
  • The lifetime is defined by the storage: "the cart exists while the row exists". The concept then has no way to say that a checked-out cart is over even though its row is still there for analytics.
What the move costs
  • Deferring survival keeps V0 small and guarantees that V2 and V3 each break an assumption V0 relied on; the breaks are planned, but they are still work.
  • A cart whose owner is a session id is easy in V1 and becomes the merge problem in V4; choosing "user only" avoids the merge and loses the anonymous shopper.
  • Writing the survival table is cheap; keeping it honest as the product changes is the same maintenance cost as any assumption register.
Misreads
  • "Persistence is not part of the concept, so ignore it." The questions are part of the concept; the answers are deferred. A definition that never asks "does it survive a reload?" produces a V0 that is also the design, and the reload bug becomes a surprise instead of a planned level.
  • "Lifetime means add a createdAt field." Lifetime is when the thing begins and ends; a timestamp is one way to implement one kind of end (expiry) and is read by no V0 operation.
  • "Own the cart means the user table has a cart column." Ownership is the relationship "this cart is findable by this shopper"; where it is encoded — a variable, a map key, a foreign key — is the representation, decided later (Representation Mapping).

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.

  • GENERALOwner and lifetime are questions every stateful concept must answer — a session, a message, a job, a rate-limit window — and the split between "part of the meaning" and "a persistence decision made later" is the same split each time.
  • STAGE-SPECIFICFor a demo, every survival answer is "no" and the lesson is a table for later; for a store with logged-in shoppers on several devices, V3 is the first version that is usable, and the deferral is measured in days rather than versions.
  • ILLUSTRATIVEThe version numbers, the stale product in storage and the anonymous cart merged at login are the concept record's invented progression; no real store's persistence is described.

Where the depth lives

This domain asks the question and hands the answer off by name.