Risk-First Development
When everything is easy except one thing, the one thing decides the project. Prototype the payment integration before polishing product cards — not because payment is most valuable, but because it is the piece whose answer could change everything else.
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.
One piece of the system is unfamiliar and the rest is routine. Where should the first week go, and what does "risk" actually mean when choosing?
The store decomposes into product pages, cart, checkout, orders, admin — all things you have done — and the payment integration, which you have not. The product pages are easy and satisfying, and you are three days into making them look good when someone asks how the payment part is going.
Build the parts you know how to build first. They produce visible progress quickly, they are pleasant, and they feel like laying a foundation: surely the store needs products before it can take money for them. The unfamiliar piece can wait until the familiar ones are solid.
Three weeks of polished catalog and cart, and the payment integration begins with the least time left and the least room to change anything. If it turns out the provider forces a hosted checkout page, the cart and checkout you built were designed for a flow that does not exist.
- Three weeks of polished catalog and cart, and the payment integration begins with the least time left and the least room to change anything. If it turns out the provider forces a hosted checkout page, the cart and checkout you built were designed for a flow that does not exist.
- The unfamiliar piece stays unfamiliar for exactly as long as you avoid it, so the estimate's widest range stays wide until the end — when a bad resolution is most expensive and there is nothing left to cut.
- Visible progress on easy pieces is read, by everyone including you, as progress on the project. The founder sees product cards and infers the store is nearly done; the one piece that could sink it has not been touched.
- Familiar work expands to fill the time. Product cards get hover states and image galleries because that is what there is to do; the polish is real work and none of it reduced the project's risk by any amount.
The move
Precisely enough to apply it to a problem you have never seen — not a slogan.
- Define risk precisely: a piece is risky in proportion to how much *the rest of the design* would change if it went badly, times how little you know about which way it will go. Payment is risky not because it is hard but because its answer — hosted page or API, synchronous or confirmed later — reaches back into cart, checkout and the order lifecycle.
- Find that piece and prototype it *before* the pieces that depend on its answer. A prototype here is not a first version; it is an experiment whose output is a decision — "this provider works like this, so checkout has to look like that" (A Prototype Answers a Question).
- Keep the prototype throwaway and surrounded by nothing. A test-mode charge and a confirmation handler with no store around them answers the question in days; the same code wired into a real cart takes weeks and answers it no better.
- Then build the familiar pieces, now shaped by what the risky one taught you. Familiar work is not wasted when it comes second; it is wasted when it has to be redone because the unfamiliar piece contradicted its assumptions.
What risk means, piece by piece
The reflex sorts pieces by comfort. The move sorts them by two numbers: how many other pieces their answer reaches, and how little is known about the answer. The matrix below is the store's pieces scored that way. Payment is not the hardest row — testing might be — but it is the only row that is both unknown *and* reaches nearly everything.
The last column is the point. A piece whose answer stays inside itself can be built late no matter how hard it is. A piece whose answer reaches into the others has to be known before they are shaped.
| Piece | How well understood | Pieces its answer reaches | Risk |
|---|---|---|---|
| Catalog | Built before | Cart (product shape) | Low |
| Cart | Built before; one open decision | Checkout | Low–medium |
| Checkout without payment | Built before | Orders | Low |
| Payment integration | Never used this provider | Cart, checkout, order lifecycle, failure handling, deployment | High |
| Order persistence | Plain CRUD | Admin | Low |
| Failure handling | Depends on payment's answer | Order lifecycle | Medium — resolves with payment |
| Admin | Built before | Nothing downstream | Low |
| Testing | Hard, but a known kind of hard | Nothing downstream | Low |
The spike, and what it is surrounded by
A spike is a slice with the store removed. It touches the provider and one handler, proves one thing, and is explicit about what it leaves unproven — which is almost everything, deliberately. The doesNotProve line is not a weakness; it is what keeps the spike a spike.
- ScriptCreates one test-mode payment for a fixed amount with no cart, no product and no user.
- ProviderResponds with either a redirect URL or a charge result — the spike's first finding.
- Confirmation handlerA single endpoint that logs whatever the provider sends afterwards, and when.
- Log lineRecords which message, from which system, was the first to say "paid".
1charge = provider.createTestPayment(amount: fixed)2log("create ->", charge.kind) // "redirect_url" or "result"3 4handler POST /provider-confirmation:5 log("confirmation <-", now(), body.status, body.reference)6 7// then, by hand: complete the redirect if there was one,8// and read the log to see which line said "paid" firstNothing here is reusable, and that is fine. The deliverable is the log and a paragraph: "hosted redirect; confirmation by webhook, delayed; order must exist before the redirect."
What the spike changed
The pieces below were on the list before the spike and are still on it. What changed is their *shape* — and the change is the return on the first week. Built in the reflex order, each of these would have been built twice.
| Trigger | Symptom | Cause | Response |
|---|---|---|---|
| Order created only after a successful charge | Customer returns from the hosted page and there is no order to show them | The design assumed a synchronous charge result; the provider confirms later | Create the order in a pending state *before* redirecting; confirmation moves it to paid (The Order Lifecycle, Built) |
| Confirmation page says "your order is paid" | Sometimes it is not yet, and sometimes it never will be | The page was designed to trust the redirect back, not the webhook | The page reports pending; a later confirmation, or a poll to the provider, upgrades it (What If Payment Fails?) |
| Webhook delivered twice | Order marked paid twice, or paid then reprocessed | Delayed confirmation implies retries; the design had no notion of duplicates | Confirmation handling keyed on the provider's reference, applied once (Duplicate Requests) |
| Local development has no public URL | Webhooks never arrive on a developer machine | Deployment was last in the order | A minimal public environment moves earlier; the deployment piece was resequenced by the spike |
How to do it
Most important first.
- For each piece in the decomposition, ask two questions: "how many other pieces change if this goes unexpectedly?" and "how sure am I which way it goes?" The piece that scores high on both is the one to spike first (The Dependency Map).
- Write the question the spike must answer before starting it, and stop when it is answered. "How does the order find out it is paid?" is a spike; "get payment working" is a project (Spikes).
- Time-box it and treat running out of time as a result: a spike that could not answer in two days has learned that the piece is bigger than it looked, which is itself the answer to the estimate question.
- Record what the spike decided and what it changed in the design, so that the familiar pieces are built against the decision rather than against the original guess (The Decision Journal).
- Then let the familiar pieces be boring. The polish you were tempted by is still available; it is now last, where its cost is visible and its value can be weighed.
Worked on a concrete problem
The move has to produce something. This is what it produced.
- The store: the riskiest piece is payment, because it is both the least known and the most consequential — hosted redirect versus API call decides whether checkout is one page or two, whether the order is created before or after the charge, and how the order learns its state. Two days of spike, no cart, no products: one test-mode charge, one confirmation handler, one log line saying which system said "paid".
- The spike finds a hosted redirect and a confirmation that arrives by webhook up to a few minutes later. Consequences, written down: the order must exist *before* the redirect, in a pending state; the confirmation page cannot promise the order is paid; there must be a path for "customer came back but the webhook has not". None of that was in the original checkout design, and all of the checkout design still to be built now knows it.
- The file-upload service, same move: the risky piece is not the upload form, it is what happens with a large file over a slow connection — resumable or not decides the API, the storage layout and the client. The spike is one endpoint accepting one large file, interrupted halfway, before any UI exists.
- What the spike did *not* do: it did not make payment production-ready, handle refunds, or verify the webhook signature. Those were on the list; the spike answered its question and stopped, and the answers shaped an estimate that had been wide by a factor of five.
How you know it worked
What now exists that did not before, and what question you can now ask.
- The first week produced a decision, written down, that the following weeks are built on — not a polished screen.
- The widest range in the estimate narrowed early, and the reason it narrowed is a fact about the provider, not a feeling about the team.
- The familiar pieces were built once, against the real shape of the risky piece, rather than built and then adjusted.
- When someone asks "how is payment going?", the answer is "we know how it works and here is what it changed", instead of "we will get to it".
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.
- ?Which piece, if it went unexpectedly, would change the most other pieces — and how sure am I which way it goes?
- ?What single question would a spike on that piece have to answer, and how would I know it was answered?
- ?What is the smallest thing I could build, surrounded by nothing, that would answer it?
- ?What did the spike decide, and which pieces still to be built now have to be shaped by that decision?
- ?Is this piece risky, or merely difficult — does its answer reach into other pieces, or only into itself?
What can go wrong
- The spike never ends. "Prototype payment first" turns into building the payment system first, complete with refunds and retries, and the store still has no products a month in. A spike answers one question; when it is answered, it stops.
- Everything is declared risky. If catalog, cart and admin all need a spike, the word has stopped meaning "could change the rest of the design" and started meaning "I would rather not commit". Most pieces are not risky, and saying so is part of the move.
- Risk is confused with difficulty. The realtime layer of the chat app is difficult; whether messages need read state across devices is *risky*, because its answer changes the data model. The difficult piece can go later if its shape is known.
- The spike's findings are not written down, and the familiar pieces are built against the original guess anyway. The risk was reduced and the design did not notice.
- A week spent on a payment spike is a week with no visible store, and stakeholders who measure progress by screens will see none. The move needs its reasoning communicated or it looks like stalling.
- Spiking the risky piece first means building it while you know the *least* about the rest of the system, so the spike may answer the wrong question. It answers it cheaply, which is the point, but it may need a second spike.
- If the risky piece turns out to be simple, the spike cost days for a decision that could have been made in an hour — and you could not have known that without doing it.
- "So always build the hardest part first." No — the *riskiest*, which is the part whose answer changes the others. "Do the hard thing first" is a slogan; made falsifiable it says "do first the piece with the widest estimate range and the most dependents", and that is often not the hardest piece.
- "Risk-first means the prototype becomes the payment module." The prototype is disposable. Its output is a decision and a paragraph, not code; if the code survives, that is a bonus, not the goal (Prototype vs Production).
- "This is the opposite of building a walking skeleton." They compose: the skeleton proves the layers connect, the spike proves the risky piece has the shape you thought. Which comes first depends on which unknown is bigger — the environment or the provider (The Walking Skeleton).
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.
- CONTESTEDThe strongest opposing view is value-first: build the piece users care about most, because a store with working products and no payment can be shown, tested with real people and funded, while a payment spike with no store can be shown to nobody — and the feedback from real use finds requirements no spike would. On a project where the risky piece is well understood by someone on the team, or where the design could absorb any of its answers, value-first is right and risk-first is caution spent on nothing (Value First).
- STAGE-SPECIFICOn a greenfield project the risky piece is usually an external integration or an unfamiliar requirement; in an existing system it is usually the place where the change touches state nobody fully understands, and the spike becomes a read-only experiment against production data rather than a prototype.
- ILLUSTRATIVEThe store, the two-day spike, the three weeks of catalog and the hosted-redirect finding are invented to show the shape of the argument; the provider is not a real one.
Where the depth lives
This domain asks the question and hands the answer off by name.