Module 6: Thinking In Bets And Assumptions
Mini-project: find `recommendations`'s riskiest assumption
Overview
It's time to bring the whole module together into a single deliverable, end to end. You learned to separate facts from assumptions with classify() (lesson 2), to organize those assumptions around a bet's thesis and not judge the decision by its result (lesson 3), to find which assumption carries the thesis's real weight (lesson 4), to quantify it with risk x impact using rankAssumptions() (lesson 5), to decide which cheap test attacks it without building everything first (lesson 6), and to calibrate RICE's confidence based on whether that test has already run (lesson 7). In this mini-project you take the recommendations bet from Mercado's backlog —the same one that's gone with the module since the introduction— and run the full pipeline: from the raw list of claims to the final recommendation of what to test and why.
And you don't just reason through it: you verify it. Your deliverable runs, with Node, four steps in sequence over recommendations's six real claims — classify() to separate fact from assumption, rankAssumptions() to order the assumptions by risk, the explicit identification of the riskiest assumption and its cheapest test, and finally a simulation of what happens once the team runs that test and gets real evidence—. It's the whole module's synthesis, applied to the case that's gone with it since the beginning, and the direct rehearsal for part of the guide's capstone (module 8), where this same backlog is going to go through opportunity sizing (module 4), RICE prioritization (module 3), MVP trimming (module 5), and cost of delay (module 7) before becoming the quarter's final roadmap.
How this connects to the module. This project closes the module by bringing its seven lessons together into a single deliverable: the fact/assumption separation (lesson 2) as the starting point, the thesis and assumption stack structure (lesson 3) as the framework, the search for the riskiest assumption (lessons 4 and 5) as the analysis engine, the cheap test (lesson 6) as the concrete action, and calibrated confidence (lesson 7) as the cycle's honest close. What you build today —a bet dissected end to end, with its most dangerous assumption named and its test plan defined— is exactly the input module 7 is going to order alongside the rest of the backlog by cost of delay, and that module 8 is going to turn into the quarter's defensible roadmap.
An analogy: opening the whole foundation, not checking a single crack
Go back to the module introduction's house. So far you worked one piece at a time: you separated facts from assumptions (lesson 2), you looked at one assumption at a time with the load-bearing block question (lesson 4), you calculated a score for each one separately (lesson 5). Today you do what a structural inspector really does before approving a construction: open the whole foundation, review every part with the same criterion, and deliver a single report that says, with no ambiguity, which part needs reinforcing before continuing to build — not a list of loose suspicions, but an ordered, actionable conclusion.
The reference solution, verified
Let's build the full solution and verify it, so you have a clear pattern. (The exercises at the end ask you to extend it and reason about it.)
Part 1 — The full claims, with risk and impact declared
Before running anything, this is the full list of claims behind recommendations, exactly as the team wrote them down — the same six from lesson 2, now with risk and impact already assigned to the ones that turned out to be assumptions:
Claim Evidence risk impact
───────────────────────────────────────────────────────────── ────────────────── ───── ──────
Mercado has 5,000 active buyers per day analytics dashboard - -
Purchase history for 80% of active buyers warehouse query - -
Users buy more if they see personalized recommendations (none) 0.6 5
The team builds the basic engine in 3 person-months (none) 0.4 2
Sellers do not complain about less visible products (none) 0.3 3
Showing recommendations does not slow down load speed (none) 0.2 3
The first two rows already have evidence — they don't need risk or impact, because classify() is going to mark them as fact from the start—. The last four are the real assumptions today's pipeline is going to rank.
Part 2 — The full pipeline, run in Node
We reuse, unchanged, classify() (lesson 2) and rankAssumptions() (lesson 5), and add the two steps that give the analysis practical closure: naming the cheapest test for the riskiest assumption, and simulating the result of running it.
// Mini-project: full pipeline over Mercado's "recommendations" bet.
// claims -> classify (fact/assumption) -> rankAssumptions (risk x impact) -> riskiest.
function classify(claims) {
return claims.map((c) => ({ ...c, type: c.evidence ? 'fact' : 'assumption' }));
}
function rankAssumptions(assumptions) {
return assumptions
.map((a) => ({ ...a, score: a.risk * a.impact }))
.sort((a, b) => b.score - a.score)
.map((a, i) => ({ ...a, riskiest: i === 0 }));
}
// --- Step 1: every claim behind the bet, unfiltered ---
const claims = [
{ text: 'Mercado has 5,000 active buyers per day',
evidence: 'analytics dashboard, updated today', risk: 0, impact: 0 },
{ text: 'We have purchase history recorded for 80% of active buyers',
evidence: 'query to the purchases table in the data warehouse', risk: 0, impact: 0 },
{ text: 'Users are going to buy more if they see personalized recommendations',
evidence: null, risk: 0.6, impact: 5 },
{ text: 'The team can build a basic recommendation engine in 3 person-months',
evidence: null, risk: 0.4, impact: 2 },
{ text: 'Sellers are not going to complain about their less popular products becoming less visible',
evidence: null, risk: 0.3, impact: 3 },
{ text: 'Showing recommendations on the homepage does not noticeably slow down load speed',
evidence: null, risk: 0.2, impact: 3 },
];
console.log('=== Step 1: classify() over "recommendations" ===\n');
const classified = classify(claims);
classified.forEach((c) => console.log(' [' + c.type.toUpperCase().padEnd(10) + '] ' + c.text));
const facts = classified.filter((c) => c.type === 'fact');
const assumptions = classified.filter((c) => c.type === 'assumption');
console.log('\n facts: ' + facts.length + ' | assumptions: ' + assumptions.length);
console.log('\n=== Step 2: rankAssumptions() over the ' + assumptions.length + ' assumptions ===\n');
const ranked = rankAssumptions(assumptions);
ranked.forEach((a, i) => {
console.log(' ' + (i + 1) + '. score=' + a.score.toFixed(1) +
(a.riskiest ? ' <-- RISKIEST ASSUMPTION' : '') + ' ' + a.text);
});
const riskiest = ranked[0];
console.log('\n=== Step 3: the riskiest assumption ===\n');
console.log(' "' + riskiest.text + '"');
console.log(' score=' + riskiest.score.toFixed(1) + ' (risk=' + riskiest.risk + ' x impact=' + riskiest.impact + ')');
console.log('\n Cheapest test that attacks it (the test\'s DESIGN, in depth, is the discovery guide\'s job):');
console.log(' show hand-curated recommendations to 50 users for 1 week and measure whether');
console.log(' they buy more than a group without recommendations -- without building any engine yet.');
// --- Step 4: the team runs that cheap test and gets real evidence ---
console.log('\n=== Step 4: the team runs the test and now there IS evidence ===\n');
const claimsAfterTest = claims.map((c) =>
c.text === riskiest.text
? { ...c, evidence: 'manual curation test, 1 week, 50 users: +18% purchase' }
: c
);
const classifiedAfter = classify(claimsAfterTest);
const assumptionsAfter = classifiedAfter.filter((c) => c.type === 'assumption');
console.log(' facts: ' + classifiedAfter.filter((c) => c.type === 'fact').length +
' | assumptions: ' + assumptionsAfter.length + ' (down from ' + assumptions.length + ')');
console.log('\n "' + riskiest.text + '"');
console.log(' went from ASSUMPTION to FACT: it now has real evidence, not just a team belief.');
What to expect. When you run the file with Node, the output is exactly this:
=== Step 1: classify() over "recommendations" ===
[FACT ] Mercado has 5,000 active buyers per day
[FACT ] We have purchase history recorded for 80% of active buyers
[ASSUMPTION] Users are going to buy more if they see personalized recommendations
[ASSUMPTION] The team can build a basic recommendation engine in 3 person-months
[ASSUMPTION] Sellers are not going to complain about their less popular products becoming less visible
[ASSUMPTION] Showing recommendations on the homepage does not noticeably slow down load speed
facts: 2 | assumptions: 4
=== Step 2: rankAssumptions() over the 4 assumptions ===
1. score=3.0 <-- RISKIEST ASSUMPTION Users are going to buy more if they see personalized recommendations
2. score=0.9 Sellers are not going to complain about their less popular products becoming less visible
3. score=0.8 The team can build a basic recommendation engine in 3 person-months
4. score=0.6 Showing recommendations on the homepage does not noticeably slow down load speed
=== Step 3: the riskiest assumption ===
"Users are going to buy more if they see personalized recommendations"
score=3.0 (risk=0.6 x impact=5)
Cheapest test that attacks it (the test's DESIGN, in depth, is the discovery guide's job):
show hand-curated recommendations to 50 users for 1 week and measure whether
they buy more than a group without recommendations -- without building any engine yet.
=== Step 4: the team runs the test and now there IS evidence ===
facts: 3 | assumptions: 3 (down from 4)
"Users are going to buy more if they see personalized recommendations"
went from ASSUMPTION to FACT: it now has real evidence, not just a team belief.
Go through the result piece by piece and recognize what each part certifies:
- Step 1 (classify). Of six claims, two were already verifiable facts and four were assumptions — the same proportion you saw in lesson 2, now as the full pipeline's entry point, not as an isolated exercise.
- Step 2 (rankAssumptions). The four assumptions get ordered with no ambiguity: "users buy more" dominates with
score=3.0, more than three times the second (0.9). No additional judgment is needed to know which one is riskiest — the number says it. - Step 3 (the riskiest and its test). The pipeline explicitly names the winning assumption and the cheapest test that attacks it — a manual curation test, without building the automatic recommendation engine, following lesson 6's rule: much cheaper than building everything, and still able to give a real signal (it compares against a group without recommendations, it isn't an opinion survey).
- Step 4 (after the test). With real evidence in hand, the same claim that started the pipeline as
assumptionends asfact— the count drops from 4 to 3 assumptions, and lesson 7'sconfidenceCeiling, recalculated with this new state, would rise from0.3to0.8, exactly as you saw in that lesson.
The full pipeline —from six raw claims to a concrete decision of what to test and why— is the module's final answer to the question the introduction opened: "what does this bet rest on, and which of those things could sink it?".
Common mistakes
Confusing "we identified the riskiest assumption" with "we already resolved it". What happens: the team runs the pipeline through Step 3, celebrates having found the riskiest assumption, and moves on to normal building without running Step 4 —the test that actually reduces uncertainty—. Why it happens: identifying and naming something feels like real progress, and sometimes it's more satisfying than the uncomfortable part of designing and running a test. How to spot it: the planning meeting ends with "we now know which the risky assumption is" as the final conclusion, with no concrete plan of when and how it's going to be tested. How to fix it: naming the riskiest assumption is Step 3 of four, not the end of the process — today's project isn't complete until Step 4 (or its real equivalent, with actual users) is actually run.
Choosing the cheapest test available in general, instead of the one that specifically attacks the riskiest assumption. What happens: instead of designing a test for "users buy more when they see recommendations" (the riskiest), the team runs the simplest test they have on hand —a quick survey about whether the feature would bother sellers— and presents it as "the module's validation progress". Why it happens: it's exactly lesson 6's mistake, repeated in the full project's context: running some test feels productive, without checking it attacks the right assumption. How to spot it: after "validating", the riskiest assumption's score in Step 2's ranking hasn't changed, even though the team did run some kind of test. How to fix it: before designing any test, verify its result, whatever it is, would directly change the tested state of the assumption that came out on top in Step 3 — if the test can't do that, it isn't this project's test.
Stopping at the ranking without connecting it to the bet's other decisions. What happens: the team produces an impeccable assumption ranking, identifies the riskiest, even runs the test — and stops there, without asking how this result changes RICE's confidence (module 3), the MVP size to build (module 5), or the bet's place in the quarter's roadmap (module 7). Why it happens: assumption analysis feels like a complete exercise in itself, and connecting its results to the ecosystem's other tools —RICE, MVP, WSJF— requires an extra step that can get overlooked. How to spot it: the team can recite a bet's riskiest assumption, but can't say what confidence that bet should have in the prioritized backlog, nor whether the test's result changes the order. How to fix it: always close the analysis by connecting it back to lesson 7 —what's the confidenceCeiling before and after the test?— and anticipate module 8 is going to demand that same connection for the quarter's full backlog.
Exercises
Exercise 1 — Recalculate confidenceCeiling after the project. Using Step 4's result (the riskiest assumption now tested, with risk: 0.6, impact: 5, tested: true, and the other three assumptions unchanged from lesson 7's original state), calculate the resulting confidenceCeiling without running Node, and compare it against module 3's original confidence: 0.5.
See solution
With the riskiest assumption now tested: true, confidenceCeiling()'s !riskiest.tested condition is false, so the function jumps straight to the final return 0.8. The result is identical to what you saw in lesson 7 with this same scenario: the ceiling rises from 0.3 (before the test) to 0.8 (after). Compared to module 3's original confidence: 0.5, today's calibrated value —0.8— is higher than the original, and this time with a concrete, verifiable justification: not "we feel more confident", but "the assumption holding up the thesis's weight now has real evidence behind it, from Mercado's own test".
Exercise 2 — Run the pipeline over a different bet. Choose sellerTools (seller tools) and write, without running Node, a list of at least 4 claims (a mix of fact and assumption, with risk and impact for the assumptions) that would support that bet. Identify which would be, per your own estimate, the riskiest assumption, and justify why.
See solution
A reasonable list:
fact: "60% of last quarter's support tickets ask for better inventory management" (with a support report as evidence).assumption: "If we give them better tools, sellers upload more products to the catalog" (risk: 0.4,impact: 4— it's, in essence, the thesis's central mechanism: if this is false, the bet loses its reason to exist).assumption: "The team can build the management panel in 2 person-months" (risk: 0.3,impact: 2— a feasibility risk, bounded in its damage if poorly estimated).assumption: "Current sellers are going to adopt the tool without needing extensive training" (risk: 0.5,impact: 3— a real usability risk, but probably solvable with better onboarding if it turns out false).
With these numbers, the riskiest assumption would be "if we give them better tools, sellers upload more products" (score = 0.4 x 4 = 1.6, the highest of the three), because it's the only one whose falsehood would make the whole bet lose its meaning —the other two, though real, are manageable even if they turn out wrong—.
Exercise 3 — Design the cheapest test for your own riskiest assumption. For the riskiest assumption you identified in exercise 2 (sellerTools), describe in 2-3 sentences the cheapest test you can think of that would give the team a real signal, without building the full management panel. You don't need to design it in depth —that's discovery's territory—; naming the general approach and why it would be cheaper than building everything is enough.
See solution
A reasonable answer: ask a small group of active sellers to upload products using a better-structured shared spreadsheet (instead of the current panel), manually assisted by someone from the team for a week, and measure whether they actually upload more products than a control group without that support. It's much cheaper than building the full panel —no software development required, just one person's time coordinating manually—, and it still gives a real signal about the thesis's central mechanism: whether better inventory management support actually changes how many products sellers upload. That experiment's fine design —how to recruit the group, how to avoid bias, how long to run it— is, as you already saw in lesson 6, product-discovery-and-prototyping-guide's job.
Summary and next step
In this mini-project you ran the module's full pipeline over the recommendations bet: classify() separated 2 facts from 4 assumptions, rankAssumptions() unambiguously identified the riskiest assumption (score=3.0, "users buy more when they see recommendations"), you named the cheapest test that attacks it without building the full engine, and you simulated the result of running it: the same claim that started as a belief ended, with real evidence, turned into a verifiable fact — ready for lesson 7's confidenceCeiling to rise from 0.3 to 0.8 with total honesty.
With this you close module 6. You can now take any bet from the backlog, separate what's known from what's believed, find the assumption that could sink it, decide how to test it cheaply and first, and calibrate your confidence in the bet based on what's actually been verified — not on how much excitement it generates in the room.
Where you go next. Module 7 takes this same analysis and adds the time dimension: cost of delay and WSJF — how much it costs to wait to test a bet's riskiest assumption, compared to others in the backlog—. Module 8, the guide's capstone, brings together everything you learned across the eight modules —outcomes over outputs, the value chain, RICE, opportunity sizing, MVP, bets and assumptions, cost of delay— into Mercado's quarter's final, defensible roadmap. What you built today —a bet dissected end to end— is exactly the piece that capstone is going to ask you for, for each of the backlog's five bets.
Resources
- Annie Duke, Thinking in Bets — annieduke.com/annie-duke-thinking-in-bets. The thesis holding up the whole module: deciding well isn't the same as getting the result right; revisit it as a closing note, with today's
recommendationsbet as your own example. In English. - David J. Bland and Alexander Osterwalder, Testing Business Ideas — summary at strategyzer.com/library/testing-business-ideas-book-summary. The full reference book for continuing to practice "assumptions mapping" on real bets, beyond Mercado's case. In English.
- Marty Cagan / SVPG, "The Four Big Risks" — svpg.com/four-big-risks. Useful as a final checklist: before closing any bet's analysis, check whether you covered all four risk types (value, usability, feasibility, business viability), not just the one that turned out riskiest this time. In English.
- Teresa Torres, "Opportunity Solution Trees" — producttalk.org/opportunity-solution-trees. The natural next step after this module: how a continuous discovery team repeats this same cycle —name, test, learn— week after week, not just once a quarter. The natural entry point into
product-discovery-and-prototyping-guide. In English.