Growing From the MVP
V1 shipped and something was learned. The next version is not "V1 plus the phase-2 list"; it is the smallest change that acts on what was learned, with each simplification undone only when its trigger fires.
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.
V1 is in front of real users. How do I decide what the next version is, and how do I keep it from becoming the full feature list I refused to build the first time?
The store is live. A few dozen orders came through, a founder is delighted, and the "not V1" list has been re-titled "V2". I have a week of feedback and a feeling that I am about to build everything I declined to build before, in the same order as before, for the same reasons as before.
Start on the phase-2 list. It was agreed, it is written down, and the MVP "worked", so the plan continues. It feels like the reward for having been disciplined.
The list was written before anyone had seen a customer, and nothing from the customers has touched it. The feedback said checkout confused people on mobile and nobody used the filter; the list says coupons and recommendations. The plan continues as if V1 had taught nothing.
- The list was written before anyone had seen a customer, and nothing from the customers has touched it. The feedback said checkout confused people on mobile and nobody used the filter; the list says coupons and recommendations. The plan continues as if V1 had taught nothing.
- Simplifications are undone all at once. Multi-currency, multi-warehouse and search all arrive in V2 because they were all deferred, though only one trigger fired. Each undoing is a migration, and three migrations at once is where the store's first serious outage lives.
- The architecture "grows up" on schedule rather than on evidence. Someone proposes that V2 is the time to split into services and add a queue, because V1 was "just the MVP". No reading justifies either; the store has a few dozen orders.
The move
Precisely enough to apply it to a problem you have never seen — not a slogan.
- Write down what V1 taught, as observations. "Orders happened, so people buy." "Half of the checkouts abandoned at the address step." "Nobody used filter-by-name." "The founder asked about a second warehouse." Each observation is evidence, and the next version is the smallest change that acts on the evidence — not the list.
- Check the triggers on the "not V1" list against the observations. A trigger that fired ("a second warehouse exists") promotes its item; a trigger that did not ("catalog too big to filter") leaves its item where it was, however senior the person who wanted it. This is what makes "iterate" mean something: an iteration that ignores what was observed is not one.
- Undo one simplification at a time, and undo the data shape first. Adding a warehouse column and backfilling the existing one is a small, boring migration; adding it during the multi-currency migration is not. Each undone simplification has an assumption it removes, and the removal is a change to find, not a feature to add (When Assumptions Change).
- Treat architecture as a simplification with a trigger, like every other. The single process stays until a reading says it is the bottleneck; a queue arrives when something must happen after the response and cannot fail silently, not when the version number changes (Add Complexity Only When Required).
The loop after a release
The reflex goes from release straight to the list. The loop below puts three steps between them, and each has a way of failing that looks like doing it.
- 1Observe
Write what users did, failed to do, asked for, and what broke — as observations, without conclusions.
fails by Writing conclusions ("we need search") and calling them observations.
- 2Check triggers
For each "not V1" item, did its trigger fire? Cite the observation.
fails by Promoting an item because its owner is senior, not because its trigger fired.
- 3Choose the smallest change
The smallest change that acts on the strongest observation — often a fix to the path.
fails by Reading one observation as a mandate for a large feature.
- 4Undo one simplification
Find the assumption comment; change the data shape in its own migration; then build on it.
fails by Undoing three at once because they were all deferred together.
- 5Release and return
Ship, and start again at Observe.
fails by Skipping Observe on the second lap because the list is now "the roadmap".
One order for the next version, and the other
Given the observations, one sequence: repair the path first, then undo the simplification whose trigger fired, then re-examine. The alternative puts the migration first and is right when the data change is the risky part.
- 1Fix the mobile address form
because The strongest observation is abandonment on the path; a leak in the path costs more than any feature adds.
- 2Warehouse table and `warehouse_id` on stock, backfilled
because The multi-warehouse trigger fired; the data shape changes in its own boring migration, with no routing logic yet.
- 3Basket-size and conversion report from existing orders
because It costs almost nothing, and it is the observation the coupon and recommendation triggers are waiting for.
- 4Return to Observe
because Three changes are enough to learn from; the list is re-checked, not continued.
Scoring the proposal to "grow up" the architecture
The matrix compares three responses to "V2 is when we add the queue and split services", scored across the standard axes for a store at a few dozen orders. The point of the matrix is the caveat under it, not the numbers.
| Option | Simplicity | Reliability | Cost | Time | Maintainability | Note |
|---|---|---|---|---|---|---|
| Stay single-process; write the trigger | Nothing changes; a sentence says what reading would change it. | |||||
| Add a queue for confirmation email only | Justified only if email must not block or be lost — is that observed? | |||||
| Split into services now | Every boundary becomes a network call with no reading that asked for it. |
caveat These are rankings at a few dozen orders, not measurements; at a load where the database is saturated the "stay" row loses its reliability score and the queue row gains one. The numbers change with the readings, which is the argument for having readings before having numbers.
How to do it
Most important first.
- Before touching the list, write the observations from V1 in one place: what users did, what they failed to do, what they asked for, what broke (The Engineering Notebook).
- Walk the "not V1" list and mark each trigger fired or not fired, with the observation that says so.
- Choose the next version as the smallest change that acts on the strongest observation — often a fix to the path, not a new feature.
- For each simplification you undo, find the assumption comment you left, and change the data shape in its own migration before building the feature on it (What Is Not V1).
- For any proposed component, ask the complexity ledger questions — what it solves, what it introduces, what happens when it fails — and require a reading, not a version number, as the answer to the first (The Complexity Ledger).
Worked on a concrete problem
The move has to produce something. This is what it produced.
- Observations after V1: people bought (the claim holds); many abandoned at the address form on phones; filter-by-name was rarely used; the founder mentioned a second fulfilment location. Triggers: multi-warehouse fired; search did not; coupons did not (there is now a baseline, but nothing suggests price is the problem). Next version: fix the address form, then the warehouse migration. Not next: coupons, search, recommendations, services.
- Undoing "one warehouse": a
warehousestable, awarehouse_idon stock, existing stock backfilled to the one location, theCHECK (stock >= 0)kept per row. No picking logic yet — the trigger was "a second location exists", not "we need to route orders". That is a later trigger with a later observation. - The architecture proposal, answered with readings: a few dozen orders, database mostly idle, checkout latency dominated by the provider round-trip. Nothing is a bottleneck. The queue is declined, with the trigger written: "when order confirmation email must not block checkout and must not be lost".
How you know it worked
What now exists that did not before, and what question you can now ask.
- The next version is named by an observation ("mobile abandonment"), not by a list position ("phase 2").
- Each undone simplification is its own migration, and you can say which trigger fired for it.
- Some deferred items were deferred again, with the trigger that still has not fired.
- The architecture is unchanged, and there is a sentence saying what reading would change 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.
- ?What did V1 actually teach, stated as observations rather than conclusions?
- ?Which triggers on the "not V1" list fired, and which did not — and what am I about to build anyway?
- ?What is the smallest change that acts on the strongest observation?
- ?Which simplification am I undoing, where is the assumption it removes, and can it be its own migration?
- ?What reading would justify the component someone is proposing, and do I have it?
What can go wrong
- Observations are collected and then overruled by the list anyway, because the list was promised. The list was a set of triggers; if it was a promise, the promise was the mistake.
- One observation is read as a mandate for a large feature. "Nobody used filter" could mean search is needed, or that products were found from the home page and filter was unnecessary. An observation is evidence for a question, not for a build.
- The smallest change is always the smallest, forever, and the product never grows past the path. At some point the claim is proven and the next claim — "people will come back" — needs its own MVP; the move restarts one level up (MVP Thinking).
- Acting on observations means the roadmap changes after every release, and people who planned around it have to re-plan.
- One migration at a time is slower in calendar terms than one big V2 migration, and safer in every other term.
- Declining architecture on evidence means being the person who says no to the exciting proposal, repeatedly, with numbers.
- "V2 is V1 plus the deferred features." V2 is V1 plus what V1 taught. Some deferred features come in; some are deferred again; some are deleted.
- "Undoing a simplification is a feature." It is a change to a data shape with a feature waiting behind it; separating the two is what keeps the migration boring.
- "Now we have users, we can scale." Now you have readings. Scale is what the readings say, and a few dozen orders say nothing.
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.
- GENERALObserve, check triggers, smallest change, one undoing at a time — this is the iteration loop for any product after any release, not only after the first.
- SCALE-SPECIFICAt a few dozen orders no reading justifies a component and the move is mostly about scope. At real load the readings start to say things, and the same move starts promoting architecture items — the loop is the same; the evidence differs.
- ILLUSTRATIVEThe dozens of orders, the abandonment at the address form and the second warehouse are invented to show the shape of the move.
Where the depth lives
This domain asks the question and hands the answer off by name.