Making Assumptions Explicit
"Only one warehouse in V1" is a fine simplification and a terrible secret. Written down with what depends on it, it becomes the reason you can later say exactly why "multiple warehouses" changes the inventory model, the checkout and the shipping estimate — instead of discovering it one bug at a time.
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.
Every design rests on things nobody checked. How do you find the ones yours rests on, and write them so that when one changes you know what has to change with it?
We built the store in a few weeks and it works. Now the founder says "we are opening a second warehouse next quarter" and I cannot say what that touches. Inventory is a number on the product. I think shipping assumes one origin. I am not sure what else assumed one warehouse, because nobody ever said "one warehouse" out loud.
Start listing the code that mentions warehouses. Grep for the word, find nothing because nothing was named, then grep for "stock" and start reading. It feels like due diligence: the change is coming, and reading the code is how you find out what it touches.
The assumption was never written into the code as a word, so reading the code finds its consequences one at a time and never the assumption itself. "Stock is a column on product" is a consequence; "shipping cost is computed from one origin" is a consequence; "the admin edits one number" is a consequence. Each is discovered separately, and the list is complete only when the last bug is.
- The assumption was never written into the code as a word, so reading the code finds its consequences one at a time and never the assumption itself. "Stock is a column on product" is a consequence; "shipping cost is computed from one origin" is a consequence; "the admin edits one number" is a consequence. Each is discovered separately, and the list is complete only when the last bug is.
- Reading finds the places the assumption shaped and misses the places it prevented. There is no "warehouse" entity to find because the assumption made one unnecessary; the biggest change — that inventory becomes a relationship between product and location — is invisible in a codebase that has no location.
- Because the assumption was silent, nobody can say whether it was a decision or an accident. "We chose one warehouse to ship faster" and "we never thought about it" lead to the same code and to very different conversations with the founder about what the second warehouse costs.
The move
Precisely enough to apply it to a problem you have never seen — not a slogan.
- Treat an assumption as a decision that was made by not deciding: something the design takes as true that a requirement could contradict. Find them by asking, for each piece of the design, "what would have to be true for this to be right?" — a stock column on a product is right only if a product has one stock, which is true only if there is one place stock lives. That chain ends in an assumption, and every chain does.
- Write each one down in the form the future needs: the assumption as a sentence, why it was made (to ship V1 small; because the founder said so; because nobody thought), and — the part that pays off — what in the design depends on it. "One warehouse: inventory is a column on product; shipping cost uses one origin; the admin stock editor edits one number; the reservation invariant is per product." That list is the change plan for the day the assumption stops being true (When Assumptions Change).
- Distinguish assumptions from requirements and from invariants. A requirement is what someone asked for; an invariant is what must never break; an assumption is what is being taken as stable without anyone having promised it (Assumption vs Requirement). "Stock never negative" is an invariant; "stock is per product" is an assumption, and it is the assumption that the second warehouse breaks.
- Keep the list where design decisions live and revisit it when a requirement arrives: the first question to ask of "we are opening a second warehouse" is "which assumptions does that contradict?", and if the list exists the answer takes a minute and names the architecture change (The Assumption Register).
The board, with the assumed column filled in
The unknowns board from the first module has a column most people leave empty. Known is what has been established; unknown is what has become a question; assumed is what is being taken as true without anyone having checked. The store's board below has the assumed column filled in the way this lesson asks — each entry carrying the reason and the dependents, so that a change to any of them names the parts of the design that move.
The unknowns on the board are the assumptions that could be checked cheaply. "Customers have accounts" is not an assumption to record; it is a question for the founder, with an experiment that takes one conversation.
- ✓V1 ships from one location; the founder confirmed this is true today.
- ✓Prices are entered and displayed in one currency today.
- ✓The payment provider is chosen and takes a currency code per charge.
- ~One warehouse (chosen, to keep inventory a number). Dependents: stock as a product column; constant shipping origin; admin stock editor; per-product reservation invariant; sold-out check; reorder report.
- ~One currency (never chosen — noticed during this pass). Dependents: price is a bare number; totals add bare numbers; the provider call hard-codes the currency; reports sum across orders.
- ~One timezone (never chosen). Dependents: order timestamps are displayed raw; the "orders today" report uses the server's day; sale start times are entered without a zone.
? Guest checkout?
becomes Must a customer have an account to place an order in V1, and if not, what identifies a cart and where does the confirmation go?
experiment One conversation with the founder; if guest checkout is wanted, a cart keyed by a session token in a spike branch to see what changes.
? Is the second warehouse real?
becomes Is a second location planned within the life of this codebase, and would it hold the same products or different ones?
experiment Ask. If yes and it holds the same products, sketch inventory as (product, location, quantity) and list which queries change.
The assumed column is the change plan in waiting: when the founder says "second warehouse", the dependents list for the first entry is the answer to "what does this touch?".
Silent and explicit, side by side
The same assumption, held two ways. The code is identical in both; what differs is whether the next engineer — or you, next quarter — can find out what depends on it without reading every consumer of the stock column.
Stock is a column on product. Shipping cost takes a destination. Nothing anywhere says "one warehouse". When the second warehouse arrives, the change is discovered by grepping "stock", reading every consumer, and finding the shipping origin last, in production, when a customer in the north is quoted a cost from the south.
The same code, plus an entry: "Assumption: one warehouse. Chosen at V1 to keep inventory a number. Depends on it: stock column; shipping origin; admin editor; per-product reservation invariant; sold-out check; reorder report. Changes when: a second location holds stock." The second warehouse is a requirement that contradicts one entry, and the entry lists what moves.
The assumption made a warehouse entity unnecessary, so the code contains no trace of it; only a record outside the code can say what the absence shaped. The dependents list turns a code archaeology exercise into a lookup.
What silent assumptions do
Each row is a store assumption that was never written down, meeting the requirement that contradicts it. The symptom is what shows up; the cause in every row is not the code that changed but the dependents that nobody knew to change with it.
| Trigger | Symptom | Cause | Response |
|---|---|---|---|
| Second warehouse opens | Stock is right in total and wrong per location; shipping quotes from the wrong origin | Inventory as a product column and a constant origin both depended on "one warehouse"; only the column was changed | Recover the dependents list; treat each as its own change (When Assumptions Change) |
| First customer pays in a second currency | Totals add euros to dollars; the provider is charged in the wrong currency | Price as a bare number depended on "one currency"; every sum in the system inherited it | Introduce money as (amount, currency) at the chokepoint; refuse mixed sums |
| Sale starts "at midnight" | Customers in one zone see it start an hour late; the "orders today" report splits a day | Raw timestamps and the server's day depended on "one timezone" | Store instants; decide the display zone per customer (Timezones and Locale Formatting) |
| Marketing wants guest checkout | Carts keyed by user id have nowhere to go; confirmations go to an account that does not exist | "Customers have accounts" shaped the cart key and the notification path | Cart keyed by a token; confirmation email captured at checkout |
How to do it
Most important first.
- For each entity and each computed value, ask "what would have to be true for this shape to be right?" and follow the chain until it ends in something nobody promised.
- Write the assumption, the reason it was made, and the list of design decisions that depend on it. The dependents are the deliverable; an assumption with no dependents listed is a note, not a tool.
- Say out loud which ones were chosen and which were never noticed. Both are fine; only the second kind is dangerous, and it is dangerous precisely until it is written down.
- Put the list next to the invariants and the requirements, and read it whenever a new requirement arrives. The first question is always "which of these does this contradict?" (What Must Be True?).
- When you make a new simplification to ship faster — one currency, one language, accounts required — write it into the list at the moment you make it, with its dependents. It costs a minute then and a week later.
Worked on a concrete problem
The move has to produce something. This is what it produced.
- The store, asked "what would have to be true for this to be right?": stock is a column on product → a product has one stock → there is one place stock lives → one warehouse. Shipping cost is a function of destination → origin is constant → one warehouse. The reservation invariant "reserved plus available never exceeds existing" is per product → one pool → one warehouse. Three chains, one assumption, never stated.
- Written down: "Assumption: one warehouse. Made: at V1 to keep inventory a number. Depends on it: inventory as a product column; shipping origin constant; admin stock editor; per-product reservation invariant; the sold-out check in checkout; the reorder report." The day the founder says "second warehouse", the list is the answer to "what does this touch?" — and the honest answer that the inventory model changes from a column to a relationship is available before anyone reads a line of code.
- The same pass on the store found two more that had never been said: "one currency" (prices are a number with no currency; totals add them; the provider is called with a hard-coded currency) and "customers have accounts" (carts are keyed by user id; there is no guest checkout; the order confirmation goes to the account email). Neither is wrong for V1. Both are now decisions with dependents rather than facts nobody chose (Dangerous Assumptions).
How you know it worked
What now exists that did not before, and what question you can now ask.
- A list exists in which each entry is an assumption, a reason and the design decisions that depend on it — and it includes at least one you had not consciously made.
- You can answer "what does a second warehouse touch?" from the list, in a minute, without reading code.
- New simplifications are written into the list at the moment they are chosen, with a note on what they buy.
- Conversations with the founder about a change now start with "that contradicts these assumptions, so it changes these parts" rather than with an estimate pulled from the air.
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 would have to be true for this shape — this column, this computation, this key — to be right, and who promised it?
- ?Which design decisions depend on this assumption, and would I know them all without reading the code?
- ?Was this chosen to keep V1 small, or did nobody notice it — and does the founder know which?
- ?When a new requirement arrives, which assumptions on my list does it contradict?
- ?Could this assumption be checked cheaply today, so that it becomes a fact instead of a record?
What can go wrong
- Everything is listed as an assumption, including "the database will be available" and "users will use a browser", and the list is too long to read when a requirement arrives. The useful entries are the ones a plausible requirement could contradict within the life of the system.
- Assumptions are listed without dependents, so the list says "one warehouse" and the change still has to be found by reading code. The dependents are the entire value.
- The list is written once and never read. It is a tool for the moment a requirement arrives; if that moment does not start with the list, the list did nothing.
- The move is used to avoid deciding: "we'll note it as an assumption" becomes a way to skip the conversation that would have settled whether a second warehouse is a real plan. An assumption that could be checked cheaply today should be checked, not recorded.
- Writing dependents down is real work, and on a prototype that will be discarded it is work spent documenting a building about to be demolished.
- Making an assumption explicit invites the stakeholder to argue with it now — "why only one warehouse?" — which is a conversation a silent assumption would have postponed, and only postponed.
- A list of assumptions can read as a list of things the system cannot do, and needs framing as the record of what V1 deliberately kept simple.
- "Assumptions are bad; the goal is to have none." A design with no assumptions has decided everything, including things nobody asked for. "One warehouse" is a good assumption for V1; the goal is to know it is there.
- "If the assumption is written down, the change is cheap." The change is *findable*. The inventory model still goes from a column to a relationship, and that is still a migration; the list tells you the cost honestly, it does not reduce it (Reversible vs Irreversible Decisions).
- "Explicit assumptions are what the design doc is for." A design doc records what was decided; assumptions are what was taken as true without deciding, which is exactly what design docs omit. They are a separate list because they answer a separate question.
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 design takes something as true that nobody promised; the chain "what would have to be true for this to be right?" ends in an assumption for a compiler's symbol table as surely as for a store's stock column.
- STAGE-SPECIFICGreenfield: assumptions are written at the moment they are chosen, and the list is short and fresh. Existing system: the list has to be recovered by reading the design backwards, and the most important entries are the ones nobody who is still on the team ever chose.
- ILLUSTRATIVEThe store, the founder, the second warehouse next quarter and the three chains that end in it are invented to show the shape of the move; a real store carries more assumptions than three.
Where the depth lives
This domain asks the question and hands the answer off by name.
- — The engineering notebook at /thinking/notebook has an assumptions field beside the decisions; the manifesto's /manifesto/layers describes the same move for the layers a framework assumes on your behalf.