RequirementsGENERALSTAGE-SPECIFICILLUSTRATIVE

Requirements Emerge During Implementation

Some requirements cannot be found by asking; they appear when the first slice runs and the code asks a question nobody had. That is not a failure of discovery — it is the other half of it — and the move is to notice the question, write it down as a requirement, and decide rather than improvise.

The moveWorked exampleNext questions

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.

The question

When building the first version reveals a requirement nobody stated, how do you tell a real requirement from an implementation detail, and what do you do with it?

The situation

You are building "create order from cart". Halfway through, the code needs to know: does the order store the product's current price or a copy of the price at checkout? You look for the answer in the requirements and it is not there. It is not in the ticket, the canvas or the stakeholder's head. The code is waiting.

The reflex

Decide on the spot, whichever way the code makes easy, and keep going. A foreign key to the product is one line; copying the price is a new column and a new field. The one-line version keeps the momentum, and it can always be changed later.

Why it stalls

The decision made by momentum is a requirement made by accident. Six weeks later an admin changes a price and every historical order's total changes with it; the accounting export is wrong; and nobody remembers that this was decided, because it was never decided — it was defaulted.

What the reflex produces — and fails to produce
  • The decision made by momentum is a requirement made by accident. Six weeks later an admin changes a price and every historical order's total changes with it; the accounting export is wrong; and nobody remembers that this was decided, because it was never decided — it was defaulted.
  • Because the question was never written down, it is asked again by the next person to touch orders, who defaults the other way, and the system now has both behaviours.
  • The stakeholder never hears the question. "Does an order remember the price it was sold at?" is a product decision with legal consequences, answered by whichever engineer happened to be typing.
  • The discovery is treated as a failure of planning — "we should have caught this" — so the next project spends longer in analysis and still meets the same kind of question, because this kind can only be met by building.
ProblemUnderstandRequirementsConstraintsUnknownsDecompositionSmallest StepModelExperimentObserveDebugLearnIterate

The move

Precisely enough to apply it to a problem you have never seen — not a slogan.

  • Accept that a category of requirements is only visible from inside the implementation: the questions the code asks when it needs a value, a default, a rule, or a decision about what happens between two steps. No amount of up-front asking finds all of them, because the person asking does not yet know there are two steps there. The move is not to prevent them; it is to recognise them when they arrive.
  • When the code asks a question you cannot answer from the requirements, stop and classify it. Is it an implementation choice — both answers give the same observable behaviour and the choice is about code shape? Or is it a requirement — the two answers are observably different to a customer, an admin, an accountant or a future engineer? The test is whether someone outside the code would ever notice which way it went.
  • If it is a requirement, write it down as one, in the three-part form, and decide it the way requirements are decided: with the stakeholder if it is a product decision, with a written reason if it is yours to make. Then keep building. The decision takes minutes; the writing is what keeps it from being re-decided.
  • If it is an implementation choice, choose and move on — but if choosing it was hard, that is a signal that it is a requirement in disguise, and worth a second look.

The slice that asks questions

The slice below is the one that produced the price question. Every layer is thin, and the point of listing what it proves and does not prove is that the "does not prove" column is where the emergent requirements live: the slice cannot prove anything about price changes because building it was the first time anyone noticed price changes were a question.

Create order from cart
A customer with a cart clicks "Place order" and an unpaid order exists.
  1. PageShows the cart and a Place order button; on success shows the order number.
  2. APIPOST /orders with the cart id; returns the order id or a reason it could not be created.
  3. LogicRejects an empty cart; captures each item's price and quantity; creates the order with status unpaid.
  4. DatabaseInserts one order row and one row per item, with the captured price.
proves
The layers connect; an order can be created from a real cart; the captured-price decision is now in the schema rather than in someone's head.
does not prove
Anything about payment, stock, concurrent checkouts or what happens when the process dies between the two inserts. It also does not prove the captured price is correct under discounts, because nobody has decided whether V1 has discounts — that question is in the notebook.

Requirement or implementation choice

The pipeline is what happens each time the code asks something the requirements did not answer. The whole thing takes minutes when it works and weeks when it is skipped, and the difference is the second step.

When the code asks a question
  1. 1
    Notice

    The code needs a value, a rule or a default that no requirement supplies. Write the question before the answer.

    fails by Typing the easy answer without registering that a question was asked.

  2. 2
    Classify

    Would anyone outside the code notice which way it went? Yes: requirement. No: implementation choice.

    fails by Classifying by convenience — everything is an implementation choice when you want to keep moving.

  3. 3
    Route

    Requirement: product decision goes to the stakeholder with a recommendation; engineering decision gets a written reason. Choice: decide and continue.

    fails by Asking the founder about column types, or deciding refund policy at the keyboard.

  4. 4
    Default provisionally

    Take a named, findable default so the slice can continue while the answer is pending.

    fails by A silent default that becomes permanent because nobody can find it.

  5. 5
    Merge back

    After the slice, add every emergent requirement to the list in three-part form.

    fails by A notebook full of answers and a requirements list that still says what it said last week.

The pipeline runs many times per slice. It is fast because most questions are implementation choices; it matters because the few that are not decide things like whether an order remembers its price.

What emergent requirements look like in the wild

The table collects the shape of the failure: a question the code asked, answered by momentum, surfacing later as something that looks like a bug and is actually an undecided requirement. Every row is a question that discovery could not have found because it lives between two steps that did not exist until the code was written.

Requirements decided by default
TriggerSymptomCauseResponse
An admin changes a product's priceEvery historical order's total changes; the sales report no longer matches the paymentsThe order referenced the product instead of capturing the price — decided by whichever was one line shorterCapture the price at checkout; treat "what does an order remember?" as a requirement and write it down
A customer's client retries a failed sendThe same chat message appears twiceNobody had imagined the retry until the send code had to handle failure; deduplication was never a requirementClient-generated message ids; a written requirement that a message is shown once
A competitor watches order numbersDaily order volume is publicSequential ids were the database default; nobody asked whether the id was visibleRandom public ids; a requirement that order volume is not inferable from identifiers
Two admins edit the same productOne edit silently disappearsThe update code overwrote the row; "what if two people edit?" only became visible when the second admin existedDecide the requirement — last write wins, or detect the conflict — and write it down either way

How to do it

Most important first.

  • Build the first slice with a notebook beside it. Every time the code needs something the requirements did not say, write the question before writing the answer (The Engineering Notebook).
  • Ask of each question: would anyone outside the code notice the difference between the two answers? If yes, it is a requirement. Write it in the actor-behaviour-result form.
  • For product decisions, send the question the same day with your recommended answer and the consequence of each option. Do not wait for the reply to keep building something else.
  • Record the default you took while waiting and mark it as provisional in the code — a named constant, a comment with the question — so it can be found when the answer arrives.
  • After the slice, re-read the requirements list and add what emerged. The list after the first slice is more accurate than the list before it, and that is the expected direction.

Worked on a concrete problem

The move has to produce something. This is what it produced.

  • The price question. Both answers produce a working order today; they diverge the day a price changes. An accountant would notice, a customer disputing a charge would notice, a future engineer writing the sales report would notice. It is a requirement: "an order records the price of each item as it was at checkout, and later price changes do not alter existing orders." The stakeholder confirmed it in one message. The column was added; the slice continued (Snapshots vs References).
  • The same slice asked three more questions. "Can a cart with zero items create an order?" — observable, a requirement, decided no. "Is the order id sequential or random?" — a customer would see it and a competitor could count orders from sequential ids; a requirement with a security angle, decided random. "Does the order row hold the total or compute it from items?" — nobody outside the code would notice; an implementation choice, computed, moved on.
  • On the chat app, building "send message" asked: "if the same message arrives twice because the client retried, do we show it twice?" Observable; a requirement nobody had written because nobody had imagined the retry until the code had to handle a failed send. Written down, decided (once), and the client now sends a message id.
  • The notebook after the first order slice had seven questions: four requirements, three implementation choices. All four requirements were added to the list; two had been sent to the founder and answered; the list was longer and truer than the one the slice started from.

How you know it worked

What now exists that did not before, and what question you can now ask.

  • Questions the code asked are written down before they were answered, and each is classified as requirement or implementation choice with a reason.
  • The requirements list grew after the first slice, and the additions are things no one could have listed from the sentence alone.
  • Product decisions that emerged were sent to the stakeholder with a recommendation, and building continued on something else while waiting.
  • Provisional defaults can be found in the code by name, not by archaeology.

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.

Next questions
  • ?What question is the code asking me right now that the requirements do not answer?
  • ?Would anyone outside the code — a customer, an admin, an accountant, a future engineer — notice which way I answered it?
  • ?If it is a requirement, whose decision is it, what do I recommend, and what can I keep building while I wait?
  • ?What did this slice add to the requirements list, and has the list been updated?

What can go wrong

How the move itself fails
  • Every question stops the build. Classifying takes a minute; deciding a product question can take a day, and the move says to keep building something that does not depend on the answer. A slice stalled on a question that has a provisional default is analysis paralysis with a notebook.
  • Everything is classified as an implementation choice, because that is the classification that lets you keep typing. The test is external observability, not convenience.
  • Emergent requirements are used as an argument against up-front discovery. Both halves are needed: discovery finds what could be asked, implementation finds what could not, and skipping the first makes the second longer and more chaotic.
  • The notebook is kept and never merged back. The requirements list still says what it said before the slice, and the next person builds from the stale version.
What the move costs
  • Stopping to classify every question slows the first slice; a builder who knows the domain well will recognise most of them without the ritual.
  • Sending product questions as they emerge produces a stream of small interruptions for the stakeholder; batching them is calmer and means building longer on provisional defaults.
  • Accepting that requirements emerge means accepting that the plan was incomplete, which is harder to say to some stakeholders than "we should have caught it".
Misreads
  • "So up-front discovery is pointless." Discovery finds the requirements that can be asked for; this lesson is about the ones that cannot. Skipping discovery does not move those questions earlier, it adds the askable ones to the pile.
  • "Any question the code asks is a requirement." Most are not. Whether the total is stored or computed is an implementation choice and treating it as a requirement is how a team ends up asking the founder about column types.
  • "Just decide and refactor later." Refactoring fixes code shape; it does not recover a decision that was never made. The order that lost its historical price is not refactored back, it is reconstructed from backups — if there are any.

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.

  • GENERALCode asking questions the requirements did not answer happens in every domain; the classification test — would anyone outside the code notice? — is the same for a store, a compiler pass or a data pipeline.
  • STAGE-SPECIFICOn a greenfield slice most emergent questions are genuinely new; in an existing system many were answered long ago by code that nobody documented, and the move becomes reading the existing behaviour before deciding — the default may already be a requirement in disguise.
  • ILLUSTRATIVEThe seven questions, the price column and the founder's one-message reply are invented to show the shape of the move; the categories are real.

Where the depth lives

This domain asks the question and hands the answer off by name.

Further
  • The engineering notebook at /thinking/notebook is where the questions the code asks are written before they are answered.