Dependency First
If B requires A, build or understand A first. Obvious when said, skipped constantly in practice: checkout built before the cart exists, payment integrated before anyone knows what an order is, a feature started on top of a concept nobody has understood.
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 are about to build something and part of it depends on something else that does not exist or that you do not understand. How do you find the dependency before it finds you, and what do you do about it?
You are building checkout. Halfway through the total calculation you realise there is no cart to total — you had been passing a hard-coded list — and that the discount rule you were applying depends on a customer type the data model does not have. You have written a lot of checkout and none of it can run.
Stub what is missing and keep going. A fake cart, a hard-coded customer type, a mocked payment result: the checkout code keeps growing and the stubs can be replaced later. Stopping to build the cart feels like leaving the thing you were doing.
The stubs make assumptions the real thing will not honour. The fake cart is a list of product ids; the real cart carries quantities and a price captured at add time, and the total calculation written against the stub is wrong in a way no test on the stub can show.
- The stubs make assumptions the real thing will not honour. The fake cart is a list of product ids; the real cart carries quantities and a price captured at add time, and the total calculation written against the stub is wrong in a way no test on the stub can show.
- Checkout cannot be run against anything real, so it cannot be observed, so every decision in it is made by imagination. It is the plan-then-build failure at the scale of one feature.
- The missing concept — customer type — gets invented in passing to unblock checkout, without anyone asking whether the store has customer types at all. A data-model decision is made as a side effect of a stub.
- When the cart is finally built, the checkout has to change to fit it, and the work looks like rework because it is: checkout was built on an assumption about its dependency instead of on the dependency.
The move
Precisely enough to apply it to a problem you have never seen — not a slogan.
- Before building anything, name what it requires: which data must exist, which other piece must work, which concept must be understood, which external answer must be known. The requirement is a dependency, and a dependency that does not yet exist or is not yet understood is built or understood first (The Dependency Map).
- Distinguish the two kinds of dependency. "Checkout requires a cart" is a build dependency: the cart must exist. "Checkout requires knowing who says an order is paid" is an understanding dependency: nothing has to be built, but a question must be answered. Both go first; they go first in different ways — one as a slice, one as a spike.
- Treat a dependency discovered mid-build as a signal to stop and go one level down, not as a gap to stub. The stub is sometimes right — for an external system you cannot run locally — but it must be a deliberate substitute with its assumptions written down, not a hard-coded list that becomes load-bearing.
- When two things depend on each other, or on a third that is unknown, the dependency map has told you where the unknown is. Cycles and shared unknowns are the riskiest places in the map, and the sequence should reach them early (Risk-First Development).
The store, as a map of what requires what
The diagram is the requires lines drawn. An arrow means "needs to exist or be understood first". Two things are worth noticing: the payment spike has no incoming arrow, so it can happen on day one although the payment integration cannot; and the evaluation of the sequence is now mechanical — anything with no unbuilt incoming arrows is buildable now.
Build dependencies and understanding dependencies
The two kinds are handled differently, and confusing them produces both classic failures: treating an understanding dependency as a build dependency yields "read everything first"; treating a build dependency as an understanding one yields a stub that thinks it is a design. The rows below are the ones the store surfaced.
| Piece | Requires | Kind | Handled by |
|---|---|---|---|
| Checkout total | A cart with quantities and captured prices | Build | Build the cart first, thin: add, remove, quantity. |
| Checkout total | Whether the price is the add-time or checkout-time price | Understand | Decide with the founder while building the cart; write it as an assumption (Snapshots vs References). |
| Payment integration | An order to attach the payment to | Build | Checkout and order persistence before the integration. |
| Payment integration | Which system is authoritative for "paid" | Understand | A day-one spike: test-mode charge, confirmation handler, log line. |
| Customer-type discount | Customer types in the model, a rule, a founder decision | Both, and unasked | Out of V1 until the founder asks for it with a rule attached. |
When the stub becomes load-bearing
Stubs are not the problem; unlabelled stubs are. The table records how a stub that was never named as one turns into a design decision nobody made, and what the response is in each case.
| Trigger | Symptom | Cause | Response |
|---|---|---|---|
| Cart stubbed as a list of product ids | Checkout totals are wrong once the real cart carries quantities and captured prices. | The stub had a convenient interface, not the real one, and the total was written against it. | Give stubs the real interface; better, build the thin cart first — it is smaller than the checkout that depends on it. |
| Customer type hard-coded to unblock a discount | A "customer type" column appears in the schema with no rule for assigning it. | A data-model decision was made as a side effect of unblocking code. | Remove the column; the discount leaves V1 until its dependencies exist (The Assumption Register). |
| Payment mocked as "always succeeds" | Checkout creates the order before payment; the provider turns out to require a pending order first, and the sequence is inverted. | The understanding dependency — order before or after payment — was mocked away instead of answered. | Run the spike; then the mock can honour the real sequence (Which Dependency Must Answer Before the User Can Be Told Anything?). |
How to do it
Most important first.
- For each piece you intend to build, write one line: "requires: …". Data, other pieces, concepts, external answers. If the line is empty, check again; almost nothing requires nothing.
- Sort by the requires lines. Anything whose requirements all exist is buildable now; anything else waits for its requirements or is spiked to understand them.
- When a requirement is a concept — "customer types", "what paid means" — ask whether it can be answered in an hour or needs an experiment, and do that before the code that depends on it (Unknown, Question, Experiment).
- When you must stub, write the stub's assumptions where the real implementation will be forced to read them, and give the stub the real interface, not a convenient one.
- Revisit the requires lines after each loop; a running system surfaces dependencies the paper version missed.
Worked on a concrete problem
The move has to produce something. This is what it produced.
- Checkout, with its dependencies named. Requires: a cart with quantities and add-time prices (build); a product with stock (exists); a rule for what the total is (understand — are prices at add time or checkout time?); knowing whether the order exists before or after payment (understand — depends on the provider). Two things to build or settle before checkout, and neither is checkout.
- The order the dependencies impose. The cart first, with the price question decided as part of building it, because the cart is where the price is captured. Then the payment spike, because "does the order exist before payment?" changes the checkout's shape. Then checkout, against a real cart and with the order-before-payment answer known. The hard-coded list never existed.
- The customer-type discount, examined. Requires: customer types in the data model (does not exist), a rule for assigning them (unknown), and a founder decision on whether V1 has them at all (not asked). Three missing dependencies for one discount rule: it leaves V1 and returns as a requirement with a source, if it returns.
- The AI assistant over company documentation. "Answer questions from the docs" requires: the docs retrievable by relevance (build), a way to tell whether an answer was grounded in a retrieved passage (understand), and an evaluation set of questions with known answers (build, and it depends on nothing, so it goes first). The retrieval step is where most teams start; the evaluation set is what the retrieval step requires to be observed at all.
How you know it worked
What now exists that did not before, and what question you can now ask.
- Every piece under construction has a written requires line, and everything on it exists or has a spike in progress.
- Stubs, where they exist, have the real interface and a comment naming their assumptions; none of them is a hard-coded list that the code has grown around.
- Concept-level dependencies — what "paid" means, whether prices snapshot — were settled before the code that embeds them was written.
- When a dependency is discovered mid-build, the response is to stop and go down a level, and it is recognisable as the method working.
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 does this piece require — which data, which other piece, which concept, which external answer?
- ?Of those requirements, which exist, which must be built, and which must be understood?
- ?If I am stubbing something, what assumptions is the stub making that the real thing might not honour?
- ?Where in the dependency map are the cycles and the shared unknowns, and how early does my sequence reach them?
What can go wrong
- The dependency map becomes the deliverable. Every piece has a requires line, the lines form a graph, the graph is drawn, and nothing has been built. The map exists to say what to build now; once it does, build it.
- Dependency-first taken as layer-first. "Everything requires the database" becomes "build the entire schema first", which is true of the product row and false of the order table you do not yet understand. Dependencies are between capabilities and concepts, at the grain of the current step.
- Understanding dependencies inflated into prerequisites. "Checkout requires understanding payments" becomes "read everything about payments first". It requires one answer — who says it is paid — and the spike that gives it.
- Stubbing forbidden entirely. An external provider that cannot run locally must be stubbed to make the loop runnable; the rule is that the stub is deliberate and its assumptions are written, not that it does not exist.
- Going down a level when a dependency appears means leaving the feature you were in, and the context switch is real. Sometimes finishing a thin, honestly-labelled stub and returning tomorrow is the better trade.
- Settling concept dependencies first — the price rule, the customer types — means talking to the founder before code, and that conversation may not be available today.
- A dependency map drawn on paper is wrong in places; the running system will correct it, and the correction costs rework the map promised to avoid.
- "Dependency-first is just topological sort." Sorting the build dependencies is the easy half. The understanding dependencies — the questions that must be answered before a piece can be built well — are the half that decides whether the sequence de-risks anything, and they do not appear in a graph of features.
- "Never stub anything." A payment provider is stubbed in every local run. The rule governs how: real interface, written assumptions, replaced deliberately.
- "If B requires A, A must be complete before B starts." A must exist well enough for B to run against it. A cart that adds and removes is enough for checkout to begin; the cart's refresh persistence can follow.
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 piece of every system requires something; the move is the same for a feature, a module, a pipeline stage or a proof. What varies is how many of the requirements are understanding rather than build.
- STAGE-SPECIFICIn a greenfield store most dependencies do not exist yet and the move sequences building them; in an existing system most exist and the move becomes finding which of them the new feature assumes and whether the assumption holds — a reading task rather than a building one.
- ILLUSTRATIVEThe half-built checkout, the hard-coded list, the customer-type discount and the documentation assistant are invented to show the shape of a dependency being found late; no real project is described.
Where the depth lives
This domain asks the question and hands the answer off by name.
- — The Decomposition Visualizer at /thinking/decompose shows a tree; the requires lines here are the arrows between its leaves, and the lab at /thinking/first sorts by them.