ExperimentsGENERALILLUSTRATIVECONTESTED

Prototype vs Production

A prototype is allowed to ignore polish, scaling, error handling and extensibility — that is what makes it fast. The condition is that it says, in writing, what it ignored and therefore what it does not prove. A prototype that does not say so becomes production by accident, and the omissions become bugs nobody chose.

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

What is a prototype allowed to skip, what is it never allowed to skip, and how do you stop it quietly becoming the product?

The situation

The upload prototype worked and the founder saw it. "Great — ship it." You know it has no authentication, no cleanup of abandoned parts, no handling of a failed part, and a hard-coded bucket name. You also know that saying "it isn't ready" sounds like the thing engineers always say, and that the founder's question is reasonable: it works, so what is missing?

The reflex

Harden it in place. Add auth to the prototype, add the cleanup, replace the hard-coded bucket — the prototype becomes the feature one patch at a time. It feels efficient: the code exists, and rewriting working code is the classic waste.

Why it stalls

The prototype's shape was chosen for the question, not for the product. Its single endpoint that does everything was fine for an afternoon; as the feature it becomes the endpoint that does everything plus auth plus cleanup, and its shape is never reconsidered because it was never chosen.

What the reflex produces — and fails to produce
  • The prototype's shape was chosen for the question, not for the product. Its single endpoint that does everything was fine for an afternoon; as the feature it becomes the endpoint that does everything plus auth plus cleanup, and its shape is never reconsidered because it was never chosen.
  • The omissions were never listed, so they are discovered one by one as bugs. Abandoned parts fill the bucket in the second month; nobody decided to skip cleanup, it was simply not part of the question.
  • "It works" and "it is done" are confused, because the prototype demonstrated the first and the demonstration was read as the second. Every later "we need to add X" sounds like scope creep to the person who saw it work.
  • Time is spent arguing about readiness in the abstract. Without a list, "not ready" is a feeling against "it works", and the feeling loses.
ProblemUnderstandRequirementsConstraintsUnknownsDecompositionSmallest StepModelExperimentObserveDebugLearnIterate

The move

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

  • Separate two questions that the prototype answers together: *does the approach work?* and *is this code the product?* The prototype answers the first and is silent on the second. Keeping them separate is the whole move; the rest is mechanics.
  • Make the prototype's omissions explicit, as a list, at the time it is built — ideally before. Polish, scaling, error handling, extensibility, security, observability, cleanup: for each, "ignored, because the question did not need it". That list is the prototype's honest scope; without it, the prototype claims everything it does not deny.
  • Distinguish what a prototype *may* ignore from what it may *never* ignore. It may ignore polish, scale, extensibility and most error paths. It may not ignore correctness of the thing it is measuring — a prototype that lies answers the wrong question — and it may not ignore a security property if the question is about a security boundary. "It's just a prototype" is a statement about scope, not a license.
  • Decide the prototype's fate on purpose: delete it, keep it as documentation, or promote it — and promotion means the omission list becomes the work list, estimated as such, with the shape of the code reconsidered against the product rather than the question (Growing From the MVP).

What a prototype may and may not ignore

The matrix is the honest scope of a prototype, one concern per row. The middle column is what makes prototypes fast; the right column is what makes them true. The right column is short on purpose, and it is never empty.

ConcernA prototype may ignore it when…A prototype may never ignore it when…
Polish and UXalmost always — the question is rarely about how it looksthe question is about whether a user can complete a flow
Scalingthe question is about behaviour, not capacitythe question is "does it hold at N?" — then N must be real
Error handlingthe errors are outside the questionthe question is about the error — resume, retry, timeout
Extensibilityalways; a prototype is not a platform— (never required of a prototype)
Securitythe prototype runs in isolation against test resources, and the omission is written downthe question is about a security boundary, or the prototype touches real data or real users
Correctness of the measurementalways: a stubbed provider, a small file, a fake clock make the answer a lie
Observabilitythe console is enough for an afternoonthe question is about what the system does under conditions you cannot watch directly

Promoting a prototype, shown two ways

The pair shows the same prototype meeting the same "ship it". The difference is not the code — it is whether the omissions were said out loud before the demo, and whether the code's shape was reconsidered before the first patch.

The prototype meets "ship it"
Harden in place
Add an auth check to the signing endpoint. Add a cron that lists the bucket and deletes old parts. Move the bucket name to config. Ship. Discover in month two that a failed part leaves an upload that can never complete, that the cron deletes parts of active uploads, and that nobody can say which product an upload belongs to.
Promote from the answer
Write the seven omissions down. Decide the boundary from the answer: the server signs, persists the multipart identifier against a product, and completes; the provider's lifecycle rule expires incomplete uploads. Design the endpoints to that shape. The prototype's script is consulted for the multipart sequence and otherwise discarded.

The prototype's single endpoint had no place to put "which product" or "which upload identifier" because its question never needed them; adding auth and cleanup to that shape produces a cleanup that cannot tell active from abandoned. Reconsidering the shape against the product costs a day and removes the class of bug; hardening in place saves the day and buys the bug.

How prototypes become production by accident

Each row is a mechanism rather than a mistake — a way the drift happens to careful people. The responses are all the same move in different clothes: say what was ignored, and decide the fate.

The drift from prototype to product
TriggerSymptomCauseResponse
A stakeholder sees the demo before hearing the question"It works, ship it" — and "not ready" sounds like stallingThe demo answered "does it work?" and was heard as "is it done?"Question and omission list first, demo second, every time
The prototype is hardened in placeEvery added concern fights the shape; cleanup deletes live partsShape chosen for the question, never reconsidered for the productDecide the boundary from the answer, then design; consult the prototype, do not extend it
The omissions were never listedAbandoned parts fill the bucket in month two, as a surpriseNobody chose to skip cleanup; it was outside the questionWrite the omission list with the question, before building
The prototype faked its hard caseMultipart requirement discovered in productionA small file stood in for the large one; the answer was to an easier questionNever skip the correctness of the measurement; run the case the question names
The prototype is deleted with its learningThe next engineer rebuilds the same afternoonCode and answer treated as the same artefactDelete the code; keep question, answer and omissions in the notebook

How to do it

Most important first.

  • Write the omission list when you write the question. "This prototype ignores: auth, cleanup, part-failure handling, configuration, cost." It takes two minutes and it is the document that later stops the argument.
  • Sort each omission into "may ignore for this question" and "must not ignore even here". A load prototype must not fake the load; a security prototype must not stub the check (What Cannot Be Simplified).
  • Show stakeholders the question and the answer, not the demo alone. "We proved direct upload works. Here is what we did not prove. Here is what the feature needs." The demo then supports the sentence instead of replacing it.
  • If promoting, reconsider shape before adding anything: does the single endpoint survive contact with auth and cleanup, or does the product need a different boundary? Answer that before the first patch (Where Does My System End?).
  • Estimate the omission list as real work. The prototype took an afternoon; the list is usually most of the feature, and saying so early is what stops "ship it" from meaning "ship the afternoon".
  • When a prototype is kept as documentation, label it so in the repository — a README that says "this answers question X; it is not the feature; see the omission list" — because the next reader will otherwise assume it is the feature.

Worked on a concrete problem

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

  • The upload prototype's omission list, written on the day: authentication (anyone can request a signed URL); abandoned-part cleanup; handling of a failed part mid-upload; configuration (bucket name hard-coded); cost tracking; mobile; tying the upload to a product record. Seven items. The prototype itself was one endpoint and one page.
  • Sorted: all seven may be ignored *for the question* "does direct upload with resume work?" — none of them changes that answer. But one of them, auth, is a security boundary: the prototype was safe only because it ran locally against a test bucket, and the omission list says so in those words, so that "ship it" cannot be read as "ship it without auth".
  • The founder conversation: "Direct upload works — the afternoon proved that. It does not prove it is safe to expose, that abandoned uploads will not fill the bucket, or what it costs. Those seven items are the feature; the first three are days each, the rest are smaller." The demo is shown *after* that sentence, and "ship it" becomes "how long for the first three?"
  • Fate: deleted, kept in the notebook as the question, the answer and the seven omissions. The production design starts from the boundary decision — the server signs, never carries bytes — and gives the signing endpoint a proper shape: authenticated, tied to a product, with the multipart identifier persisted so that cleanup and completion can find it. None of that resembled the prototype's code, and that was the point.

How you know it worked

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

  • Every prototype has an omission list written before or with it, and the list is longer than the prototype.
  • "It works" and "it is done" are said as different sentences, and stakeholders have heard both.
  • The prototype's fate — delete, document, promote — was decided rather than defaulted, and if promoted, its shape was reconsidered first.
  • You can say, for any prototype, what it may ignore and what it may not, and why the second list is not empty.

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 did this prototype ignore, and did I write that down before someone saw it work?
  • ?Which of these omissions could the prototype legitimately skip for its question, and which would have made its answer a lie?
  • ?Is this code's shape the product's shape, or the question's shape — and which do I want?
  • ?What is the fate of this prototype, and did I choose it?

What can go wrong

How the move itself fails
  • The omission list is used to avoid ever shipping. Seven items become forty, each one "must be done first", and the feature that a prototype de-risked in an afternoon takes a quarter. The list is the work; the MVP decision still says which of it is V1 (What Is Not V1).
  • The distinction is applied to the wrong thing. A walking skeleton is not a prototype — it is the first slice of the product and should be built to be kept; treating it as throwaway wastes the skeleton (The Walking Skeleton).
  • The prototype is deleted and the learning with it. Delete the code; keep the question, the answer and the omissions somewhere the next engineer will look.
  • "Must not ignore" grows until the prototype is the product. A prototype that must handle every error is not fast; the list of things a prototype may never skip is short — the correctness of its own measurement and any security property under test — and should stay short.
What the move costs
  • An omission list is a document that stakeholders can read as a list of problems, and a demo without one is more persuasive in the moment.
  • Deleting a working prototype and re-deriving the feature from its answer costs the time the prototype's code would have saved — sometimes real, usually smaller than it looks.
  • Being precise about "not done" can make the engineer the person who always says no; the list is what makes the no specific, and specific is the only kind that survives.
Misreads
  • "Prototypes can skip correctness." A prototype can skip correctness *of the parts outside the question*. A prototype that fakes its own measurement — a stubbed provider, a small file standing in for a large one — has skipped the correctness of the answer, and the answer is worthless. The slogan "an MVP doesn't need to be correct" is precise only as "an MVP may be incomplete; it may not be wrong about what it claims".
  • "Never promote a prototype." Promote it when its shape happens to be the product's and the omission list is short — it happens. The rule is that promotion is a decision with the list in hand, not a default.
  • "The omission list is the definition of done." It is the definition of *not done yet*. What V1 actually includes is a separate decision, made against the list rather than instead of it.

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.

  • GENERALThe separation of "does the approach work?" from "is this code the product?" applies to any prototype in any domain; what a prototype may never skip differs — a performance prototype may not fake load, a security prototype may not stub the check — but the omission list is universal.
  • ILLUSTRATIVEThe seven omissions, the "days each", the test bucket and the founder's "ship it" are invented to show the move producing a conversation; no real feature's estimate is being quoted.
  • CONTESTEDA strong school holds that most prototypes *should* be promoted: in a product whose requirements are still unknown, the prototype is the only code that has met reality, its "wrong shape" is a guess about a product that may not exist, and rewriting it is design-before-evidence in disguise. On that view the omission list is a backlog, not a reason to start over, and the discipline is to keep the prototype small enough that promoting it is safe. The lesson's position is that promotion is fine when chosen with the list in hand — the disagreement is about the default.

Where the depth lives

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

Further
  • The manifesto's "What Are You Delegating?" cards at /manifesto/delegating are an omission list for a library: what it handles, what stays yours. A prototype's omission list is the same document for your own code.