Spikes
A spike is a time-boxed investigation whose only output is reduced uncertainty: a question, a box of hours, an answer, and then a decision to discard or integrate. The time-box is what distinguishes it from research that never ends, and the written answer is what distinguishes it from a branch someone will find later and wonder about.
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.
There is something you need to understand before you can design the next piece. How do you investigate it without the investigation becoming the project?
Checkout needs the payment provider, and you have never integrated one. You do not know whether the provider confirms a payment synchronously in the API response or asynchronously by calling you back, and the two make checkout a completely different shape. You could read the documentation for a day, or build a tiny integration, or both — and you have a week for the whole of checkout.
Read everything, then build. Open the provider's documentation, the tutorials, three blog posts and the SDK reference, and start integrating when you feel you understand. It is conscientious, and understanding before building is right — it is just that "feel you understand" has no end.
The reading has no stopping rule, so it stops when the week runs out. Two days in, you understand the provider's dispute process and its multi-currency handling and still have not answered the one question that decides checkout's shape.
- The reading has no stopping rule, so it stops when the week runs out. Two days in, you understand the provider's dispute process and its multi-currency handling and still have not answered the one question that decides checkout's shape.
- The understanding is untested. The documentation says the confirmation is asynchronous; it does not say what happens when the callback arrives before your own request has returned, and only a running integration shows that.
- The investigation becomes an integration. Halfway through "just trying it", the try has a database table, error handling and a retry loop, and it is now the checkout branch, built before the question was answered.
- Nothing is written down. Next month, someone asks "why does checkout wait for a callback?" and the answer is in the head of whoever spent that week.
The move
Precisely enough to apply it to a problem you have never seen — not a slogan.
- Name the uncertainty as a question whose answer would change a design decision — not "learn the payment provider" but "does the provider confirm synchronously or by callback, and can the callback arrive before my request returns?" A spike has exactly one such question; a second question is a second spike (Unknown, Question, Experiment).
- Put a box around it: a number of hours, decided before starting, after which the spike ends whether or not it answered. The box is the discipline; without it a spike is just research. If the box ends without an answer, the finding is "this question is bigger than it looked" — which is itself an answer, and the next step is to split the question, not to extend the box.
- Investigate in the way that answers fastest, which is usually a mix: read only the section that addresses the question (A Reading Strategy for an Unfamiliar Library), then build the smallest thing that observes the behaviour — a test-mode charge and a callback receiver with a log line, nothing else.
- End the spike with a written answer, what it changes about the design, and a decision about the code: discard it (usual), keep it as documentation, or integrate it — integration meaning the spike's code is reshaped to the design, not that the design is reshaped to the spike.
The unknown, before and after the box
A spike is what happens to one row of the unknowns board. The board below shows the payment unknown as it was first said, as it was sharpened into a question with a box, and — in the known column — what the four hours produced. The remaining unknowns are on the board with boxes of their own, not being investigated this week.
- ✓Final payment confirmation arrives by callback; the API response is provisional.
- ✓The callback can arrive before the starting request returns, so the order must exist with the provider reference before the charge is started.
- ✓Test mode reproduces both behaviours, so the design can be tested without money.
- ~One payment per order in V1; partial payments and split tenders are out of scope and written down.
? Refunds.
becomes Does a refund reverse the original charge by reference, or create a new transaction — and which event tells us it succeeded?
experiment A later spike, two hours: refund a test-mode payment and log every event that follows; not this week, because it does not change checkout's shape.
? What if the callback never comes?
becomes After how long should an order with a started charge and no callback be treated as failed, and can we ask the provider for the status instead of waiting?
experiment One hour: find the provider's status-query endpoint and call it for a payment whose callback was dropped by our own handler; then decide the timeout from what it returns.
? Duplicate callbacks.
becomes Does the provider retry callbacks, and does a retried callback carry the same event identifier?
experiment Have the callback handler return an error once and watch whether, and how, the event arrives again; compare identifiers.
Three unknowns remain and each has a box. None of them is being spiked now, because none changes this week's design. The board says which are next.
Where the spike sits in the week
The order of work for checkout puts the spike before the design that depends on it and after the parts that do not. That ordering is the reason spikes are cheap: four hours before the design is worth days after it. The alternative order is real, and the device says when it is right.
- 1Checkout without payment: validate cart, total, create order
because Nothing here depends on the provider, and an order that exists before payment turns out to be exactly what the spike demands.
- 2Spike: sync or callback, and can the callback race? (four-hour box)
because The answer decides the shape of everything that follows; four hours before is cheaper than a redesign after.
- 3Design the payment step from the spike's answer
because Order persisted with the provider reference first; callback handler finds it by reference; the response is provisional.
- 4Integrate payment in test mode, against the design
because The spike's code is consulted for the call sequence and otherwise not reused; the design is the source of shape.
- 5Spike: dropped and duplicate callbacks (one-hour boxes), then handle them
because These change the handler's details, not the design's shape, so they come after the shape exists.
The spike as a loop
Every spike runs the same short loop, and each step has a characteristic way of failing. The loop is what keeps a spike from being "look into payments"; the failure column is what to watch for on the third hour.
- 1Question
One question whose answer changes a design decision
fails by "Learn the provider" — no answer could end it
- 2Box
Hours decided before starting; a stopping observation named
fails by A generous box, extended when "almost there"
- 3Read to the question
The sections that address it; nothing else yet
fails by Reading the disputes chapter because it was next
- 4Observe
The smallest running thing that shows the behaviour, with timestamps
fails by The observer grows a database and becomes the integration
- 5Answer, consequence, fate
A sentence, a design consequence, and discard / document / integrate
fails by Nothing written; branch left open; re-spiked next month
If the box ends at step four without an observation, the honest output is "the question was two questions" — write that, split it, and box each half.
How to do it
Most important first.
- Write the question and the box on the same line before starting: "Sync or callback, and can the callback race my response? — four hours." If the box feels too small, the question is probably two questions.
- Decide the stopping condition: what observation ends the spike? "I have seen a test-mode payment confirmed, and I know which channel confirmed it first." A spike without a stopping condition ends when you are tired.
- Read to the question. The documentation's page on confirmation and the page on callbacks; skip disputes, currencies and reporting until a question needs them.
- Build to the question. One endpoint that starts a test payment; one that receives the callback; a log line with a timestamp on each. No database, no order, no store.
- Write the answer as a sentence and the design consequence as another: "Callback; and yes, it can arrive first. Therefore the order must exist and be findable by the provider's reference before the charge is started." Put both in the notebook (The Engineering Notebook).
- Decide the code's fate and act on it the same day. A spike branch left open becomes the thing someone integrates by mistake.
Worked on a concrete problem
The move has to produce something. This is what it produced.
- Question: "Does the provider confirm payment in the API response or by callback, and can the callback arrive before my request returns?" Box: four hours. Stopping condition: a test-mode payment observed as confirmed, with timestamps showing which channel reported it first.
- Hour one: the documentation's confirmation page says both — the response carries a provisional status, the callback carries the final one. That is already a design consequence: the response cannot be trusted as "paid". Hour two and three: an endpoint that starts a test payment, an endpoint that logs the callback. Hour three: the callback arrives while the starting request is still awaiting the provider's response. Observed, timestamped, answered.
- Answer, written: "Final confirmation is by callback; the callback can precede the API response. Therefore the order must be created and persisted with the provider reference *before* the charge is started, and the callback handler must find it by that reference; if it cannot, the callback must be safely retried later." Discard the code — it is two endpoints and a log line, and the design it implies is different in shape.
- The alternative spike that was *not* run: "how do refunds work?" It is a real unknown; it does not change checkout's shape this week; it is on the board with a box for later. One spike, one question, and the week still has four days for checkout.
How you know it worked
What now exists that did not before, and what question you can now ask.
- The spike has a question and a box written before it starts, and a stopping condition you could recognise.
- It ended inside the box with either an answer or a split question — and either way, something was written down.
- The design consequence is stated as a sentence a reviewer could disagree with.
- The spike's code has a fate, and the branch is not still open.
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 is the one question whose answer would change the design, and what observation would answer it?
- ?How many hours am I giving it, and what will I do if the box ends first?
- ?Is this something reading can settle, or does it need to be observed?
- ?What does the answer change about the design, and what happens to the spike's code?
What can go wrong
- The spike never ends. Four hours become four days because the answer is "almost there", and the box was decorative. A box that is extended once will be extended again; split the question instead.
- The spike becomes the integration. The test-mode endpoints acquire a database table and error handling, and the checkout that results has the spike's shape rather than the design's.
- Everything is spiked. A question the documentation answers in one paragraph gets a four-hour box; the spike is for uncertainties that *reading cannot settle* — behaviour under timing, under load, under this particular combination.
- The answer is not written down, so the spike is re-run by the next person, or its conclusion is remembered wrongly.
- A time-boxed spike can end without an answer, and the hours are then spent on knowing the question was too big — real information, but it does not feel like it.
- Building to observe is slower than reading to believe, and for questions the documentation answers reliably the spike is wasted effort.
- Discarding spike code that works feels wasteful, and the pressure to integrate it is proportional to how well it works.
- "A spike is a small prototype." Close, and the difference matters: a prototype answers "does this approach work?"; a spike answers "what is true here?" so that an approach can be chosen. Spikes often precede prototypes (A Prototype Answers a Question).
- "The time-box should be generous so the spike can finish." The box should be small enough that missing it is informative. A generous box is research with a calendar.
- "Reading is not a spike." Reading to a question with a box and a written answer is a spike; the move is about the question, the box and the answer, not about whether code was written. Most spikes on unfamiliar technology are half reading, half observing.
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.
- GENERALA question, a box, an observation and a written answer is how any uncertainty is investigated — a provider, a library, a query planner, a browser API, a colleague's system; the box size and the mix of reading and building vary, the structure does not.
- ILLUSTRATIVEThe four-hour box, the hour-by-hour account, the callback racing the response and the week for checkout are invented to show the shape of a spike; no specific provider's behaviour is being described.
- TEAM-SPECIFICOn a team the spike's written answer is the deliverable, because someone else will build on it; a solo learner can hold the answer in their head for a day but not for a month, and the notebook is the substitute for the colleague who would have asked.
Where the depth lives
This domain asks the question and hands the answer off by name.