Choosing a Representation
Only after the state and the operations are known can "array or map or rows?" be answered — because the answer is whichever fits the access pattern the operations describe, and before the operations exist there is no access pattern to fit.
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 what the cart must remember and what can happen to it. How do you decide how to hold it in memory — and why is that question unanswerable any earlier?
I have written down that a cart is a collection of items, each a product id and a quantity, and that the operations are add, remove, change quantity, view and total. Now I have to type the first line of code and I am stuck on whether items should be an array, an object keyed by product id, a Map, a class with a list inside it, or a table. Every tutorial does it differently and I cannot tell which one is right.
Pick the one the last tutorial used, or the one the language makes easiest — an array, because [] is short — and move on. The choice feels arbitrary, so any choice feels as good as any other, and choosing at all feels like the code starting.
The representation is chosen before the operations are listed, so it is chosen for the first operation someone thinks of — usually "show the items" — and the operation that actually stresses it, "add a product that is already there", arrives later and has to be bent around the structure.
- The representation is chosen before the operations are listed, so it is chosen for the first operation someone thinks of — usually "show the items" — and the operation that actually stresses it, "add a product that is already there", arrives later and has to be bent around the structure.
- Because the choice was arbitrary, it cannot be defended. When a reviewer asks "why an array?" the honest answer is "the tutorial had one", and the reviewer's "why not a map?" gets "I suppose we could" — and the conversation produces a rewrite rather than a reason.
- The tutorial that used a map had a different concept: a lookup table of products keyed by id. Copying its representation copies its access pattern into a cart that has a different one, and the mismatch surfaces as awkward code — converting to an array to render in order, converting back to look things up.
- Nothing has been learned about the cart. The structure was chosen by what was at hand, so when the requirement changes — carts with thousands of lines, or a cart that must survive a reload — there is no record of which assumption the current structure depended on, and the change is a guess again.
The move
Precisely enough to apply it to a problem you have never seen — not a slogan.
- Refuse the question until the two things it depends on exist: the state (what must be remembered — What Must It Remember?) and the operations (what can happen to it — What Can Happen to It?). A representation is a way of holding state so that the operations are cheap and the rules are easy to keep; without state and operations there is nothing for it to be good at.
- Read the operations as an access pattern. Each one either looks something up by a key, walks everything, appends, removes or changes one thing in place. Write, next to each operation, which of those it is. The cart's add looks up by product id and then either changes in place or appends; remove looks up and deletes; view and total walk everything in order.
- Hold each candidate representation against that pattern and ask what each operation costs and what each rule costs to keep. A collection of CartItem can be an array, a map from product id to item, or rows in a table; each makes some operations cheap, some rules structural, and some things — order, serialisation — free or awkward.
- Choose the one whose cheap operations are the frequent ones and whose structural guarantees are the rules you most need, and write the reason down next to the choice, including the case where you would switch. The reason is the deliverable; the structure is a consequence of it.
Why the question has to wait
The order below is the implementation track's order, and the representation sits sixth for a reason that the device makes visible: every step before it produces an input the choice needs. Meaning says what the thing is; state says what it remembers; operations say how the state is accessed; rules say what must hold; examples say what the operations do on real values. Choose a structure before those and you are choosing for a cart you have not described.
The alternative order is real and the device names it. When the representation is forced from outside — a legacy table, a framework's store, a message format — it is a constraint, not a choice, and the operations are discovered against it.
- 1Meaning — what a cart is, before any code
because Identity, ownership and lifetime decide whether the thing needs an id and an owner at all, which is state the representation must hold.
- 2State — what it must remember, every field challenged
because A representation of unchallenged state holds a stored total and a product name that will be removed, and is redone when they are.
- 3Operations — what can happen to it, each as inputs, reads, changes, output
because The reads and changes are the access pattern; this is the input the choice consumes.
- 4Rules and examples — what must hold, on concrete values
because A rule the structure can enforce is a reason to prefer that structure; an example says which operations are frequent.
- 5Representation — array, map or rows, with the reason and the switch condition
because Now there is an access pattern to fit and a set of rules to make structural.
- 6Pseudocode and one operation in code
because The algorithm is written against a structure whose cost is known, so
findin the pseudocode is a scan on purpose.
cart_item table or as the shape a payment provider expects, take the representation as given and discover the operations against it. You would choose this in an existing system, where the question is not "which structure?" but "which of my operations does this structure make awkward, and is that awkwardness worth a migration?"Three ways to hold a collection of CartItem
The same state — a collection of product id and quantity pairs — can be held three ways, and the device below says when each fits and what it costs. None of them is wrong; each is wrong for a different access pattern, which is why the pattern has to exist before the choice does.
Notice what the costs are about. They are not about elegance or what the language prefers; they are about which operation becomes a walk, which rule becomes structural, and what happens at the boundary where the cart is serialised or rendered in order.
Which structure fits the operations the cart already has?
when The collection is small, it is rendered in the order things were added, and it crosses a JSON boundary often. The frequent operation — add — scans for an existing entry, and at cart size the scan is invisible.
cost Finding an existing product walks the array; "one entry per product" is a line of code rather than a property of the structure, so the bug that skips it is possible.
when Lookup by product id dominates, the collection can grow large, or key uniqueness must be structural — a server holding many carts keyed by owner, a wishlist with thousands of lines.
cost Serialising to JSON needs a conversion; insertion order is kept only where the language guarantees it (JavaScript Maps do, C++ unordered_map does not); "the cart in the order added" may need a second structure.
when The cart must survive a restart or be seen from more than one device. The unique constraint enforces the rule against every client, including buggy ones.
cost Every operation is a round trip; two tabs adding the same product now race, and the concurrency that did not exist in memory has to be handled.
Asking the question so it can be answered
The stuck form of the question — "should I use an array or a map?" — has no answer because it names no operation. The device below shows the same need asked three ways, and why the best form is the one that can be settled by looking at the operations you already wrote down rather than by taste.
The best form also carries its own switch condition. A question that mentions the size and the rule can be re-asked when either changes, which is what makes the answer revisable instead of permanent.
why The best form can be answered by reading the operations list — the lookup, the walk, the order, the boundary and the size are all in it — and it names the conditions under which the answer flips, so the choice comes with its own revisit trigger. The vague form invites an opinion; the best form invites a comparison that two people can do and agree on.
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 choosing, list the operations in a column and, beside each, its access pattern: lookup by key, walk all, append, remove, change in place. If the column is empty, go back to What Can Happen to It?; the representation question is premature.
- Mark which operations are frequent. In a cart, add and view dominate; total runs on every render; remove and change quantity are rare. The frequent ones decide.
- List the rules the representation could enforce for you. "One entry per product" is structural in a map and in a table with a unique constraint; in an array it is a line of code you have to remember (Rules Determine Implementation).
- For each candidate, write what each frequent operation costs and what is awkward — a map needs a conversion step to become JSON; an array needs a scan to find an existing product. Then choose, and write the sentence "we chose X because Y; we would switch to Z if W" (Array Cart vs Map Cart).
- Do not choose rows yet unless persistence is already a requirement. Rows are a representation for a cart that must survive; the in-memory representation is chosen first and the rows are its mapping later (Representation Mapping).
Worked on a concrete problem
The move has to produce something. This is what it produced.
- The cart's state, already discovered:
items, each withproductIdandquantity; name, price and total derived from the catalog rather than stored. The operations: add (look up by product id, then change in place or append), remove (look up, delete), change quantity (look up, change in place or delete), view (walk all, in order), total (walk all). - Candidates held against that pattern. Array of CartItem: add scans for the existing product — a walk, not a lookup — then appends; view is a walk in insertion order for free; JSON for free. Map from product id to CartItem: add is a key lookup; the "one entry per product" rule is structural; view needs the language to keep insertion order and JSON needs a conversion. Database rows: every operation is a round trip, but the unique constraint on (cart_id, product_id) enforces the rule against any client.
- The choice, with its sentence: "Array of CartItem, because a cart holds a handful of items and is shown in the order they were added; the scan on add is invisible at that size and the array serialises for free. We would switch to a map if carts grew to thousands of lines or if key uniqueness had to be structural, and to rows the moment the cart must survive a restart." That is the shopping-cart concept's own
because, and it was reachable only after the operations existed.
How you know it worked
What now exists that did not before, and what question you can now ask.
- Each operation has an access pattern written beside it, and the frequent ones are marked; the representation was chosen to make those cheap rather than to match a tutorial.
- The choice comes with a sentence naming the reason and the condition under which it would change — and someone else could read the sentence and disagree with the reason rather than with the taste.
- The rules have been sorted into "structural in this representation" and "a line of code I must remember", so the code review knows where to look.
- You can answer "why not a map?" in one breath, and the answer is about the cart, not about the language.
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.
- ?What are the operations, and which of them are frequent?
- ?For each operation, is it a lookup by key, a walk over everything, an append, a removal or a change in place?
- ?Which of my rules could the representation enforce for me, and which would stay as code I must remember?
- ?Does order matter, do duplicates matter, how large can this get — and which of those would flip my answer?
- ?What sentence explains this choice, and what condition would make me switch?
What can go wrong
- The comparison is done for every candidate a search returns — array, map, set, linked list, sorted map, trie — for a concept with five items and three operations. The move is meant to take minutes for a cart; when two candidates clearly fit, choose the simpler one and write down why.
- The operations are listed but their frequency is not, so the representation is optimised for the rare operation. A cart chosen for fast removal has optimised the operation nobody performs.
- The reason is decided but not written, and a month later the structure is "just how it is". The sentence with the switch condition is the part that survives; the choice alone is a fact without a cause.
- The move is applied to state that has not been challenged yet. A representation chosen for a cart that stores product names and a total is a representation for the wrong state, and it will be redone when the state is (Challenging Unnecessary State).
- Refusing the question until the operations exist delays the first line of code; for a concept as small as a counter, the delay is longer than the coding would have been.
- Writing the access pattern beside every operation is bookkeeping that a fluent engineer does in their head; on paper it looks like ceremony until the day the requirement changes and the paper says which assumption broke.
- Choosing the simpler representation with a written switch condition means accepting that a switch may happen — a rewrite that a more general first choice might have avoided, at the cost of carrying generality nobody needed.
- "So the array is the right answer for carts." It is the right answer for a cart with a handful of items rendered in order. For a wishlist with thousands of entries, or a server holding many carts by owner, the map fits the pattern better; the lesson is the method, not the verdict.
- "I should design the database table first, since that is the real representation." Rows are the representation of a cart that must survive, and that is a persistence decision made later (The Persistence Ladder). The in-memory shape is chosen from the operations; the table is a mapping of it, and choosing the table first bends the domain logic around SQL.
- "The representation is an implementation detail I can change later." Often true — if the operations are behind functions so callers never touch
itemsdirectly. It stops being true the moment the array leaks into the UI and the API response; then the representation is a contract, and this is why Operation Contracts come before it.
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.
- GENERALThe dependency — representation after state and operations — holds for any concept: a queue, a session, a document. What differs is how many candidates are worth comparing; for most application concepts it is two.
- STAGE-SPECIFICOn a greenfield concept the representation is chosen; in an existing system it is inherited, and the move becomes reading the operations to check whether the inherited structure still fits them — which is how "why is this a map?" gets answered.
- ILLUSTRATIVEThe cart with its handful of items, the wishlist with thousands of entries and the five operations are the shopping-cart concept record's own numbers, used for the shape of the argument rather than as measurements.
Where the depth lives
This domain asks the question and hands the answer off by name.
- — The manifesto's layers page at /manifesto/layers shows the same choice from the other side: a data structure is a layer, and choosing it before the layer above has been described means you are guessing the interface.