Module 8: Project Prioritize Mercados Roadmap

Cut each bet down to the MVP that tests its riskiest assumption

Description

You already know each of the five bets' riskiest assumption (lesson 5). This lesson's question is module 5's question, applied to all five at once: what's the smallest thing that puts that specific assumption to the test, without building the whole bet? You're going to reuse compareApproaches to compare, bet by bet, the cost and the timing of the evidence between building everything and building the MVP — and selectMinimalScope to cut, feature by feature, one of the five's (sellerTools) proposed backlog down to its minimal core.

Connection to the module. This is Layer 5 of the seven. Notice an important detail about how it connects to the previous lesson: each bet's riskiestAssumption field, in today's code, is literally the text that won in rankAssumptions in lesson 5 — not a new or generic assumption. This isn't a coincidence: an MVP without a specific riskiest assumption behind it isn't an MVP, it's just "something small and cheap" (as module 5 lesson 2 warned). This lesson's result —each MVP's effort in person-days— is also the direct input for the jobSize you're going to use in lesson 7 to sequence the roadmap.

Worked example: compareApproaches over the five bets

Each bet brings two plans —build everything, or the MVP— and its riskiest assumption, taken directly from lesson 5:

// L06: we cut the 5 bets down to the MVP with compareApproaches (module 5),
// reusing each one's riskiest assumption (lesson 5).
function compareApproaches(bet) {
  const { name, riskiestAssumption, buildEverything, mvp } = bet;
  const effortRatio = Math.round((mvp.effort / buildEverything.effort) * 100);
  const sameEvidence = buildEverything.provesAssumption === mvp.provesAssumption;
  return {
    bet: name,
    riskiestAssumption,
    buildEverything: { effort: buildEverything.effort, evidenceAt: buildEverything.evidenceAt },
    mvp: { effort: mvp.effort, evidenceAt: mvp.evidenceAt },
    effortRatio,
    sameEvidence,
  };
}

function summarizeBets(bets) {
  const results = bets.map(compareApproaches);
  const totalBuildEverythingEffort = results.reduce((sum, r) => sum + r.buildEverything.effort, 0);
  const totalMvpEffort = results.reduce((sum, r) => sum + r.mvp.effort, 0);
  const totalSaved = totalBuildEverythingEffort - totalMvpEffort;
  const totalSavedPercent = Math.round((totalSaved / totalBuildEverythingEffort) * 100);
  return { results, totalBuildEverythingEffort, totalMvpEffort, totalSaved, totalSavedPercent };
}

const mvpBacklog = [
  {
    name: 'fasterCheckout',
    // winning assumption from lesson 5 (riskScore: 0.54)
    riskiestAssumption: 'cutting from 5 to 3 steps removes real friction that causes abandonment, not just cosmetic steps',
    buildEverything: { effort: 18, evidenceAt: 'day 18', provesAssumption: true },
    mvp: { effort: 3, evidenceAt: 'day 3', provesAssumption: true }, // A/B of the 3-step flow, 10% of mobile web traffic
  },
  {
    name: 'recommendations',
    // winning assumption from lesson 5 (riskScore: 0.45)
    riskiestAssumption: 'showing complementary products in the cart makes more buyers add a second product before paying',
    buildEverything: { effort: 40, evidenceAt: 'day 40', provesAssumption: true },
    mvp: { effort: 4, evidenceAt: 'day 4', provesAssumption: true }, // fixed "bought together" table, from the module 1 project
  },
  {
    name: 'sellerTools',
    // winning assumption from lesson 5 (riskScore: 0.52)
    riskiestAssumption: 'if sellers see which products are turning over slowly or running out of stock, they adjust price or stock in time, and that reduces lost sales',
    buildEverything: { effort: 25, evidenceAt: 'day 25', provesAssumption: true },
    mvp: { effort: 3, evidenceAt: 'day 3', provesAssumption: true }, // weekly email report to pilot sellers
  },
  {
    name: 'reviews',
    // winning assumption from lesson 5 (riskScore: 0.47)
    riskiestAssumption: 'seeing a visible rating on the product page increases the odds that a buyer completes the purchase',
    buildEverything: { effort: 20, evidenceAt: 'day 20', provesAssumption: true },
    mvp: { effort: 3, evidenceAt: 'day 3', provesAssumption: true }, // star rating + count, seeded by hand
  },
  {
    name: 'improvedSearch',
    // winning assumption from lesson 5 (riskScore: 0.44) -- NOTE: it's the assumption about
    // the problem's DIAGNOSIS, not about whether the correction algorithm works.
    riskiestAssumption: 'most search abandonment happens because of typos or synonyms, not because of missing relevant inventory in the catalog',
    buildEverything: { effort: 35, evidenceAt: 'day 35', provesAssumption: true },
    mvp: { effort: 5, evidenceAt: 'day 5', provesAssumption: true }, // manual labeling of a sample of failed searches
  },
];

console.log('=== compareApproaches over the 5 bets ===\n');
const summary = summarizeBets(mvpBacklog);
summary.results.forEach((r) => {
  console.log(r.bet + ':');
  console.log('  buildEverything: ' + r.buildEverything.effort + ' person-days, evidence ' + r.buildEverything.evidenceAt);
  console.log('  mvp: ' + r.mvp.effort + ' person-days, evidence ' + r.mvp.evidenceAt + '  (ratio: ' + r.effortRatio + '%)\n');
});
console.log('Totals: build everything = ' + summary.totalBuildEverythingEffort + ' person-days | all 5 MVPs = ' +
  summary.totalMvpEffort + ' person-days (' + summary.totalSavedPercent + '% of the total)');

What to expect. Running the file with Node, the output is exactly this:

=== compareApproaches over the 5 bets ===

fasterCheckout:
  buildEverything: 18 person-days, evidence day 18
  mvp: 3 person-days, evidence day 3  (ratio: 17%)

recommendations:
  buildEverything: 40 person-days, evidence day 40
  mvp: 4 person-days, evidence day 4  (ratio: 10%)

sellerTools:
  buildEverything: 25 person-days, evidence day 25
  mvp: 3 person-days, evidence day 3  (ratio: 12%)

reviews:
  buildEverything: 20 person-days, evidence day 20
  mvp: 3 person-days, evidence day 3  (ratio: 15%)

improvedSearch:
  buildEverything: 35 person-days, evidence day 35
  mvp: 5 person-days, evidence day 5  (ratio: 14%)

Totals: build everything = 138 person-days | all 5 MVPs = 18 person-days (87% of the total)

With 18 person-days —less than a month of a single person's work— the team can have real evidence on all five of the quarter's bets, against 138 person-days (almost seven person-months) of building all of them in full. No individual effortRatio goes above 17%. And look closely at improvedSearch's MVP: it's 5 person-days, but not to build half a spelling-correction algorithm — it's to manually label a sample of failed searches and measure how many really are explained by typos or synonyms, versus how many fail because of missing inventory. That MVP tests exactly the riskiest assumption that won in lesson 5 —the problem's diagnosis—, not the easier and less important question of whether the algorithm works well.

selectMinimalScope: cutting down sellerTools's feature backlog

sellerTools's full plan ("a complete dashboard with real-time charts, automatic alerts, and export") can be broken down into six candidate features. We apply module 5's algorithm to verify, feature by feature, that the mvp.effort: 3 above isn't a made-up number, but the exact sum of the single feature that tests the riskiest assumption:

function selectMinimalScope(features) {
  const selected = features.filter((f) => f.provesTheBet);
  const cut = features.filter((f) => !f.provesTheBet);
  const totalCost = features.reduce((sum, f) => sum + f.cost, 0);
  const selectedCost = selected.reduce((sum, f) => sum + f.cost, 0);
  const savedCost = totalCost - selectedCost;
  const savedPercent = Math.round((savedCost / totalCost) * 100);
  return { selected: selected.map((f) => f.name), cut: cut.map((f) => f.name), totalCost, selectedCost, savedCost, savedPercent };
}

const sellerToolsFeatures = [
  { name: 'Weekly email report with the 5 worst-turnover products, generated by hand with a query', cost: 3, provesTheBet: true },
  { name: 'Real-time sales-by-product charts', cost: 8, provesTheBet: false },
  { name: 'Automatic alerts when a product runs out of stock', cost: 5, provesTheBet: false },
  { name: 'Excel/CSV export of the whole catalog', cost: 3, provesTheBet: false },
  { name: 'Historical turnover trend view (12 months)', cost: 4, provesTheBet: false },
  { name: 'Ranking of the marketplace\'s best and worst sellers', cost: 2, provesTheBet: false },
];
const scope = selectMinimalScope(sellerToolsFeatures);
console.log('selected: ' + scope.selected.join(', '));
console.log('cut: ' + scope.cut.join(', '));
console.log('total cost: ' + scope.totalCost + ' | MVP cost: ' + scope.selectedCost + ' | saved: ' + scope.savedCost + ' (' + scope.savedPercent + '%)');

What to expect. Running the file with Node, the output is exactly this:

selected: Weekly email report with the 5 worst-turnover products, generated by hand with a query
cut: Real-time sales-by-product charts, Automatic alerts when a product runs out of stock, Excel/CSV export of the whole catalog, Historical turnover trend view (12 months), Ranking of the marketplace's best and worst sellers
total cost: 25 | MVP cost: 3 | saved: 22 (88%)

Two numbers to check consistency between this lesson's two parts: total cost: 25 is exactly sellerTools's buildEverything.effort: 25 in the table above, and MVP cost: 3 is exactly its mvp.effort: 3. That's not a coincidence — it's the same figure, calculated two ways: above, as an already-decided input; here, as the exact sum of the one feature (out of six candidates) that actually tests whether sellers react to seeing their turnover data. The real-time charts, the automatic alerts, the export, the twelve-month history, and the seller ranking —five of the six features, 22 of the full plan's 25 person-days— get cut without losing any of the evidence this quarter needs: none of them is necessary to know whether a seller, on seeing one of their products turning over badly, adjusts price or stock in time.

Common mistakes

Designing the MVP for the wrong assumption. What happens: when building improvedSearch's MVP, someone designs an experiment that measures whether the spelling-correction algorithm works well technically (for example, "does it catch 90% of common typos?"), instead of measuring whether search abandonment really is explained by typos. Why it happens: the technical question is easier to turn into a traditional engineering experiment (a test-case suite) than the diagnostic question, which requires looking at real behavioral data. How to spot it: compare the designed MVP against the exact text of lesson 5's riskiestAssumption — if they don't match word for word in what they test, the MVP is testing the wrong assumption. How to fix it: as in today's example, improvedSearch's MVP isn't "half an algorithm" — it's manual labeling of real failed searches, designed exactly to answer the diagnostic question that won in lesson 5.

Cutting features at random, without the provesTheBet criterion. What happens: when cutting sellerTools's backlog, someone chooses what to cut based on which features seem "most expensive" or "hardest," not on which ones actually test the riskiest assumption. Why it happens: cutting by cost is more intuitive than cutting by evidence. How to spot it: if your team's cutting criterion is "let's remove the most expensive one" instead of "let's remove what doesn't test the assumption," you could end up cutting the one feature that matters and keeping five that add no evidence. How to fix it: the right question, for every feature, is always the same: "if this feature didn't exist, could we still conclude something about the riskiest assumption?" — if the answer is yes, cut it, no matter how much it costs.

Adding up the effortRatio of the five bets and presenting it as a single number. What happens: someone averages the five individual effortRatio values (17%, 10%, 12%, 15%, 14%) and presents "on average, an MVP costs 14% of building everything" as a general rule applicable to any future bet. Why it happens: a single summary number is easier to repeat than five different ones. How to spot it: that average figure gets used to estimate the cost of an MVP that hasn't been designed yet. How to fix it: every bet needs its own compareApproaches, with its own full plan and its own MVP designed specifically for its own riskiest assumption — today's average describes these five bets with this data, not a general law.

Exercises

Exercise 1 — Verify the total savings without recommendations. If for some reason the team decided not to build recommendations's MVP this quarter (it's left for next quarter), recalculate totalBuildEverythingEffort, totalMvpEffort, and totalSavedPercent with the remaining four bets.

See solution

Without recommendations (buildEverything: 40, mvp: 4): totalBuildEverythingEffort = 138 - 40 = 98; totalMvpEffort = 18 - 4 = 14; totalSaved = 98 - 14 = 84; totalSavedPercent = round((84 / 98) × 100) = round(85.7) = 86. The savings percentage barely changes (87% with all five, 86% with four) — that makes sense, because recommendations has, of the five, an individual effortRatio (10%) close to the group's average, so removing it doesn't distort the aggregate much. What does change is the jobSize available for sequencing in lesson 7: with four bets, the quarter has 14 person-days of MVP to allocate, not 18.

Exercise 2 — Design the MVP for a sixth bet. For "add cryptocurrency payment" (the bet from lesson 5's exercise 2, whose winning riskiest assumption was about whether real demand exists, not about the technical integration), propose an MVP plan —not the buildEverything— that puts that specific assumption to the test, with its estimated effort in person-days and its evidenceAt.

See solution

A reasonable MVP: "add the option 'Pay with crypto (coming soon)' at checkout, with no real integration behind it, that only logs how many buyers click and, ideally, asks some of them why —a one-question survey— before showing them it's not available yet" (effort: 2 person-days, evidenceAt: 'day 2'). It's a variant of a "fake door," like the one you saw in module 5: it requires no real payment gateway, and it answers exactly the riskiest assumption —does real demand exist?— without spending a single day on the technical integration, which would only make sense to investigate after confirming the demand exists.

Exercise 3 — Defend sellerTools's cut to the sellers team. The team that proposed sellerTools expected a visual dashboard with real-time charts, not a weekly email report generated by hand. Write, in 3-4 sentences, how you'd defend this cut to them, without sounding like you're taking away their idea.

See solution

A possible answer: "We're not scrapping the dashboard —we're postponing 88% of its cost (22 of 25 person-days: the real-time charts, the alerts, the export, the history, and the ranking) until we confirm the part that actually decides whether it's worth it: whether a seller, on finding out one of their products is turning over badly, actually adjusts the price or the stock in time. The weekly email report costs 3 person-days and answers exactly that question, with real sellers, in a week. If it confirms they do react, we build the full dashboard with much more confidence than if we'd done it blind; if they don't react, we save 22 person-days on a pretty alert nobody was going to use." The argument works because it doesn't downplay the original vision — it postpones it until there's evidence to justify it, in the same spirit as module 5's project.

Summary and next step

In this lesson you cut the quarter's five bets down to their MVP —the cheapest experiment that tests, specifically, each one's riskiest assumption (lesson 5)— with compareApproaches, and you verified with selectMinimalScope, feature by feature, that sellerTools's number wasn't arbitrary. The result: 18 person-days of evidence on all five bets, versus 138 person-days to build them all in full — an 87% savings, without losing the evidence that actually matters.

With each bet's real size (lesson 4) and its MVP's effort (this lesson) in hand, lesson 7 closes the circle: sequencing the quarter by cost of delay, with wsjf from module 7 — and there you're going to see, with the complete numbers, whether lesson 3's RICE order survives the four layers added since then.

Resources

  • Eric Ries, The Lean Startuptheleanstartup.com. The origin of the MVP as an experiment that validates a specific hypothesis, not a reduced version of the product.
  • Henrik Kniberg, "Making Sense of MVP" — blog.crisp.se/2016/01/25/henrikkniberg/making-sense-of-mvp. The skateboard drawing, now applied to five bets from the same quarter.
  • Basecamp, "Shape Up" — basecamp.com/shapeup. On how to decide, once the scope is already cut, in what order bets enter a real work cycle — the direct bridge into lesson 7.