Module 3: The Opportunity Solution Tree
Branching and pruning the tree
Overview
The previous lessons built Mercado's tree by adding branches, one at a time: every new opportunity, every new solution, every new candidate experiment felt like progress. And it is — but only up to a point. A real tree, with a life of its own (not just this module's diagram), tends to grow faster than a team can attend to: every week of interviews brings new opportunities, every brainstorming session brings new solutions, and if nobody cuts anything, the tree gets so wide that no team has time to run experiments for every branch at once.
This lesson names the two operations that keep a tree useful: branching (adding new opportunities and solutions, which you already did in lessons 5 and 6) and pruning (deciding, with explicit judgment, which branches to tackle first and which to leave untouched for now). Pruning isn't the same as discarding forever — a pruned branch stays in the tree, it just isn't this week's priority. You're going to see a model run that counts how wide the tree is, and another that prunes it using a criterion you already know from the previous guide: impact, one of RICE's four factors.
How this connects to the module. We reuse validateTree() from lessons 5 and 6 in spirit — today's tree goes through the same two checks—, but this lesson's focus is two new models: countBranches(), which measures how wide the tree is, and pruneTree(), which trims it down to the highest-impact opportunities. We don't recalculate full RICE here —you already did that in product-thinking-for-engineers—; we just reuse the impact field to illustrate what pruning means in practice.
An everyday analogy: the fruit tree nobody ever prunes
Think of a real fruit tree —a lemon tree, say— that nobody ever cuts a branch from. Over the years, it grows in every direction: dozens of thin branches, each competing for the same amount of sunlight and the same nutrients coming from the roots. The result, almost always, isn't more fruit — it's smaller, weaker fruit, spread across too many branches, none getting enough to grow well. A gardener who knows what they're doing cuts, every season, the weakest or least promising branches — not because those branches are "bad," but because the whole tree produces better fruit when it concentrates its energy on fewer, better-chosen branches.
A product team's opportunity solution tree behaves exactly the same way. Every new opportunity you add is one more branch competing for the same scarce resource: the team's time to run experiments. A tree with seven active opportunities, all "in progress" at the same time, doesn't produce seven solid learnings — it produces seven weak learnings, each with less attention than it would need to be reliable. Pruning —choosing, this week, two or three opportunities to tackle seriously, and leaving the rest untouched for now— is what lets the tree, at bottom, actually bear fruit.
Worked example: how wide the tree is, and how it gets pruned
We expand Mercado's tree to seven opportunities — the five you already know, plus two new ones: "I can't pay in installments without a credit card" and "the checkout has too many steps and I abandon it." We add an impact field (1 to 5, the same RICE field you already calculated in the previous guide — we don't recalculate it, we just reuse it) to each opportunity. countBranches() counts how many opportunities, solutions, and experiments the full tree has. pruneTree() keeps only the highest-impact opportunities.
// countBranches: counts how many opportunities, solutions, and experiments
// the full tree has -- a simple way to see how wide it's gotten.
function countBranches(tree) {
let solutions = 0;
let experiments = 0;
tree.opportunities.forEach((opp) => {
solutions += opp.solutions.length;
opp.solutions.forEach((sol) => {
experiments += sol.experiments.length;
});
});
return { opportunities: tree.opportunities.length, solutions, experiments };
}
// pruneTree: keeps only the top N highest-impact opportunities (the same
// impact field from RICE, from the previous guide -- we don't recalculate
// it, we just use it to sort). Pruning isn't discarding forever: it's
// choosing where to start.
function pruneTree(tree, keep) {
const sorted = [...tree.opportunities].sort((a, b) => b.impact - a.impact);
return { outcome: tree.outcome, opportunities: sorted.slice(0, keep) };
}
const wideTree = {
outcome: '+GMV via checkout conversion',
opportunities: [
{
problem: "I don't discover products I'd like without searching for the exact name",
impact: 5,
solutions: [
{ idea: 'personalized recommendations on the home page and checkout', experiments: ["fake door: 'see my recommendations' button at checkout", 'clickable prototype with 20 buyers'] },
{ idea: 'hand-curated categories by trend', experiments: ['category landing page prototype with 10 buyers'] },
],
},
{
problem: "I don't trust new sellers without reviews",
impact: 3,
solutions: [
{ idea: 'verified-seller badge on the product page', experiments: ['clickable prototype of the product page with the badge'] },
],
},
{
problem: 'I forget about my cart and never go back to finish it',
impact: 4,
solutions: [
{ idea: 'abandoned-cart email reminder', experiments: ['wizard-of-oz: send the reminder by hand to 30 users'] },
],
},
{
problem: 'add a one-click buy button',
impact: 2,
solutions: [
{ idea: 'one-click buy button on the product page', experiments: [] },
],
},
{
problem: 'comparing prices between similar products takes me a long time',
impact: 3,
solutions: [
{ idea: 'comparison table between similar products', experiments: ['clickable prototype of the comparison table', 'fake door: compare button on the product page'] },
],
},
{
problem: "I can't pay in installments without a credit card",
impact: 2,
solutions: [
{ idea: 'installment payments without a credit card', experiments: ['survey of 30 buyers about payment methods'] },
],
},
{
problem: 'the checkout has too many steps and I abandon it',
impact: 4,
solutions: [
{ idea: 'single-step checkout', experiments: ['clickable prototype of the single-step checkout'] },
],
},
],
};
console.log('=== how wide is Mercado\'s tree? ===\n');
const counts = countBranches(wideTree);
console.log('opportunities: ' + counts.opportunities);
console.log('solutions: ' + counts.solutions);
console.log('experiments: ' + counts.experiments);
console.log('\n=== pruned to the 3 highest-impact opportunities ===\n');
const pruned = pruneTree(wideTree, 3);
pruned.opportunities.forEach((opp) => {
console.log('impact ' + opp.impact + ' -- "' + opp.problem + '"');
});
What to expect. When you run the file with Node, the output is exactly this:
=== how wide is Mercado's tree? ===
opportunities: 7
solutions: 8
experiments: 9
=== pruned to the 3 highest-impact opportunities ===
impact 5 -- "I don't discover products I'd like without searching for the exact name"
impact 4 -- "I forget about my cart and never go back to finish it"
impact 4 -- "the checkout has too many steps and I abandon it"
Seven opportunities, eight solutions, nine candidate experiments — that's the tree's real size after five lessons of adding branches. No small Mercado team can run nine experiments at once with the attention each one needs; trying to would produce exactly the unpruned lemon tree from the analogy — nine weak learnings, instead of two or three solid ones. pruneTree() trims the tree down to the three highest-impact opportunities: product discovery (impact 5, the highest, and the one directly underpinning the recommendations bet), and a tie between forgotten carts and the too-many-steps checkout (impact 4 both).
Why pruning by impact isn't the same as pruning with validateTree()
It's worth looking carefully at what happened with the "add a one-click buy button" opportunity — the one lessons 4 and 5 marked [SUSPICIOUS]. In today's tree it has impact: 2, one of the lowest, so it stays out of the pruned top 3 either way. It might be tempting to conclude pruneTree() "already solves" the problem of disguised opportunities, simply by assigning them low impact. But that would be a mistake: impact is a number someone on the team assigns by hand, with their own judgment — and nothing stops someone, convinced of their idea, from having given that same suspicious opportunity impact: 5. That it ended up with low impact in this example is a useful coincidence for the lesson, not a guarantee. Pruning by impact and validating with validateTree() are two independent checks, asking two different questions: one asks "how much do we care about this, if it's real?"; the other asks "is this real, to begin with?" Both are needed, and neither replaces the other.
Common mistakes
Falling in love with one branch and abandoning exploration of the rest. What happens: the team finds an opportunity that particularly excites it —often because it already has a solution idea it really likes, like recommendations— and pours all its attention into it, while the other six opportunities in the tree get completely abandoned, without even a cheap experiment to know whether they deserve more attention. Why it happens: it's more comfortable to go deep in one known direction than to keep several branches alive at once — especially if that direction already generates genuine enthusiasm on the team. How to spot it: if you ask the team about the state of the tree's other opportunities, the answer is "we haven't looked at that again" for almost all of them, while a single one hogs all the recent conversations. How to fix it: pruning doesn't mean choosing one branch forever — it means choosing two or three for this week, with the explicit intent to revisit the rest later. Go back to the full tree periodically, not just to the favorite branch.
Leaving the tree so wide that nothing gets properly tested. What happens: in the opposite direction, the team resists pruning —"every opportunity is important, we don't want to drop any"— and ends up trying to make a tiny bit of progress on all seven at once, without dedicating enough time to any of them to get a reliable signal. Why it happens: pruning feels like giving something up, and nobody wants to be the one deciding which opportunity goes without attention this week. How to spot it: use countBranches() — if the number of active opportunities exceeds what the team can attend to with quality (for a small team, rarely more than two or three at once), the tree is too wide to produce solid learning on any branch. How to fix it: remember pruning isn't discarding forever — it's choosing an order. pruneTree() doesn't erase the opportunities outside the top N; it just leaves them off "this week's" list. They'll still be there for when the team has room.
Confusing "high impact" with "valid opportunity". What happens: seeing pruneTree()'s result, someone assumes the opportunities that survived the pruning are already guaranteed to be real and well-formed, without running them through validateTree() again. Why it happens: having survived one filter (high impact) feels like having survived every necessary filter. How to spot it: as you saw in today's example, a disguised opportunity could, in another scenario, get high impact assigned by mistake and survive the pruning without anyone noticing it's actually a solution with another name. How to fix it: always run both checks on the opportunities that survive pruning — validateTree() to confirm they're real problems, and only then pruneTree() (or the reverse order) to decide which to tackle first. No impact number replaces lessons 4 and 5's content check.
Exercises
Exercise 1 — Recalculate the pruning with a top 2. Without running Node, using today's example's same seven impact values, which opportunities would remain if pruneTree(wideTree, 2) instead of 3? Does the result change compared to the top 3 you saw run?
See solution
The top 2 by impact would be: impact 5 ("I don't discover products...") and impact 4 ("I forget about my cart..." — the first of the two impact 4 opportunities in the array's original order, since JavaScript's Array.prototype.sort is stable and keeps the relative order between elements with the same value). "The checkout has too many steps," which was in the top 3, falls out. This exercise shows something important about pruneTree(): when there are impact ties (like the two 4s in this tree), the exact result depends on the order in which the opportunities originally appear in the array — a technical detail worth knowing before blindly trusting a pruning result with ties.
Exercise 2 — Diagnose a real tree. For a team you know (or can imagine), estimate how many active opportunities its discovery tree has right now (even if they don't have it formally written down), and compare it to what the team could attend to with quality in a week. Is it closer to the unpruned lemon tree, or to a well-trimmed one?
See solution
There's no single answer —it depends on the team you pick—, but the exercise wants you to apply countBranches()'s criterion honestly, not a vague impression of "we have a lot on our radar." Many teams, doing this exercise carefully, discover they have half a dozen simultaneous "initiatives" with no active experiment behind most of them — the exact unpruned-lemon-tree pattern, even if nobody explicitly decided it that way.
Exercise 3 — Defend an unpopular pruning. Mercado's team wants to keep working on all seven opportunities at once, "so nobody is left without their favorite idea." Using today's example's exact numbers (opportunities: 7, solutions: 8, experiments: 9), write a 3-4 sentence message defending pruning down to just three.
See solution
A reasonable message: "We have seven active opportunities in the tree, with eight solutions and nine candidate experiments across all of them — if we try to move forward on all seven at once, each one will get just a fraction of our attention this week, and we'll end up with nine weak signals instead of two or three reliable ones. I propose we focus on the three highest-impact ones —product discovery, forgotten carts, and the too-many-steps checkout— and leave the other four in the tree, without deleting them, to pick back up as soon as we have evidence on these three. We aren't dropping any idea forever — we're choosing an order." The argument works because it doesn't downplay the other four opportunities — it just insists that attacking all of them at once, without focus, doesn't produce better learning than attacking a few with quality.
Summary and next step
In this lesson you named the two operations that keep a tree useful: branching (adding opportunities, solutions, and experiments — what you did in lessons 5 and 6) and pruning (choosing, with explicit judgment, which branches to concentrate this week's effort on). You saw countBranches() confirm Mercado's tree grew to seven opportunities, eight solutions, and nine experiments — too wide to tackle all at once—, and pruneTree() trim it down to the three with the highest impact. And you saw, in the final mistake, why high impact and structural validity are two independent checks, neither a substitute for the other.
Before moving on you should be able to: explain the difference between pruning and discarding forever; use countBranches() to diagnose whether a tree is too wide for a team's size; and explain why running pruneTree() doesn't replace running validateTree() over the opportunities that survive.
With this, you close module 3's body. You now have the four complete pieces: the tree's shape (lesson 2), a verified outcome at the root (lesson 3), real opportunities told apart from disguised solutions (lesson 4), solutions properly hung with no orphans (lesson 5), experiments as a candidate menu (lesson 6), and a criterion for branching and pruning (this lesson). Lesson 8, the mini-project, builds Mercado's full tree from scratch, in a single file, and makes the final call: which branch to tackle first, and with which experiment.
Resources
- Product Talk, "The Power of Opportunity Solution Trees: 7 Key Benefits Revealed" — producttalk.org/benefits-of-opportunity-solution-trees. On why keeping the tree as a living artifact —one that gets pruned and updated— is what gives it value, instead of leaving it as a fixed diagram from a single meeting. In English.
- Marty Cagan (SVPG), "Product Discovery" — svpg.com/product-discovery. You already saw it in lesson 2; it's worth rereading here, with the full tree in mind, to see how a real team decides, week by week, which opportunity to tackle first. In English.
- Teresa Torres, Continuous Discovery Habits — producttalk.org/continuous-discovery-habits. The book devotes a full chapter to prioritizing opportunities within the tree — the same problem you solved today with
pruneTree(), with much more nuance than fits in this lesson. In English.