Module 7: Strategy To Roadmap

The strategy-roadmap bridge

Description

Everything you built in this module —translating to bets (lesson 2), the filter (lesson 3), the high-RICE trap (lesson 4), coherence (lesson 5), the explicit NO (lesson 6)— converges in this lesson toward one final question: once the filter decided who competes, what decides the order? The answer isn't new — it's, literally, the riceScore you already know from product-thinking-for-engineers-guide. The only thing that changes is when it's applied: never before the filter, always after.

Connection to the module. This lesson builds no new mechanism — it explicitly connects, with code, two pieces that until now lived in different guides: strategicFilter (this guide) decides who enters the priority conversation; riceScore and prioritize (product-thinking-for-engineers-guide, module 3) decide in what order, only among those who already got in. It's the literal bridge that gives the module its name.

An everyday analogy: the relay race, qualifying before finish order

Before a relay race starts, every team has to qualify: meet the category's rules —correct number of runners, age, team weight, whatever the category requires. Qualifying doesn't care at all about how fast the team can run; it only asks whether it has the right to be on the track. Once the qualified teams are lined up at the starting line, a completely different criterion kicks in: the stopwatch, which decides, among those who qualified, who finishes first.

A team with the best practice time of the whole season, that didn't qualify because it didn't meet the rules, doesn't race — no matter how fast it is. And a team that barely qualified, with a modest practice time, does get the chance to race and, who knows, surprise everyone on race day. Qualifying and the stopwatch are two different questions, at two different moments, and no serious judge mixes them up. The strategic filter is qualifying. RICE is the stopwatch. This lesson is the track where, at last, you see the two working together, in the right order.

Worked example: the filter decides who runs, RICE decides in what order

We run the two functions in sequence, never mixing them: first strategicFilter on the complete backlog, to get the list of eligible bets; then prioritize —the same function, unchanged, from product-thinking-for-engineers-guide, module 3— applied only to that list of eligible bets, to get the final, already-ordered roadmap.

function riceScore({ reach, impact, confidence, effort }) {
  return (reach * impact * confidence) / effort;
}

function strategicFilter(backlog, strategy) {
  return backlog.map((bet) => {
    const reinforces = bet.servesDimensions.filter((d) => strategy.winOn.includes(d));
    const conflicts = bet.servesDimensions.filter((d) => strategy.avoid.includes(d));
    return {
      feature: bet.feature,
      servesDimensions: bet.servesDimensions,
      reinforces,
      conflicts,
      inStrategy: reinforces.length > 0 && conflicts.length === 0,
      riceScore: Number(riceScore(bet.rice).toFixed(2)),
    };
  });
}

// prioritize() is the same function from product-thinking-for-engineers-guide
// (module 3), unchanged -- here it's only applied AFTER the filter, not before.
function prioritize(items) {
  return items.slice().sort((a, b) => b.riceScore - a.riceScore);
}

const mercadoStrategy = { winOn: ['curatedDiscovery', 'sellerTrust'], avoid: ['price'] };

const backlog = [
  { feature: 'fasterCheckout', servesDimensions: ['convenience'], rice: { reach: 8000, impact: 2, confidence: 0.8, effort: 2 } },
  { feature: 'recommendations', servesDimensions: ['curatedDiscovery'], rice: { reach: 5000, impact: 1, confidence: 0.5, effort: 3 } },
  { feature: 'sellerTools', servesDimensions: ['sellerTrust'], rice: { reach: 1200, impact: 2, confidence: 0.8, effort: 2 } },
  { feature: 'reviews', servesDimensions: ['sellerTrust'], rice: { reach: 6000, impact: 0.5, confidence: 0.8, effort: 1 } },
  { feature: 'improvedSearch', servesDimensions: ['catalogBreadth'], rice: { reach: 9000, impact: 1, confidence: 0.5, effort: 3 } },
  { feature: 'lowestPriceMatch', servesDimensions: ['price'], rice: { reach: 9500, impact: 3, confidence: 0.8, effort: 3 } },
];

console.log('=== Step 1 (this module): the strategic filter decides WHO competes ===\n');
const filtered = strategicFilter(backlog, mercadoStrategy);
const eligible = filtered.filter((b) => b.inStrategy);
console.log('Eligible for the roadmap: ' + eligible.map((b) => b.feature).join(', '));

console.log('\n=== Step 2 (product-thinking, module 3): RICE decides the ORDER, only among eligible bets ===\n');
const roadmap = prioritize(eligible);
console.table(roadmap.map((b) => ({ feature: b.feature, riceScore: b.riceScore })));

console.log('Mercado\'s final roadmap: ' + roadmap.map((b) => b.feature).join(' > ') + '.');

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

=== Step 1 (this module): the strategic filter decides WHO competes ===

Eligible for the roadmap: recommendations, sellerTools, reviews

=== Step 2 (product-thinking, module 3): RICE decides the ORDER, only among eligible bets ===

┌─────────┬───────────────────┬───────────┐
│ (index) │      feature      │ riceScore │
├─────────┼───────────────────┼───────────┤
│    0    │     'reviews'     │   2400    │
│    1    │   'sellerTools'   │    960    │
│    2    │ 'recommendations' │  833.33   │
└─────────┴───────────────────┴───────────┘
Mercado's final roadmap: reviews > sellerTools > recommendations.

Notice the exact order of the code's two sections, because it's this whole lesson's argument, made structure: strategicFilter runs first and produces eligible — three bets, with no order among them yet. Only afterward, prioritize receives that already-filtered list, never the complete backlog, and produces the final order: reviews first (the best riceScore among the eligible bets, 2400), then sellerTools (960), then recommendations (833.33). If you compared this final roadmap against lesson 4's complete ranking —where lowestPriceMatch and fasterCheckout occupied first and second place—, you'll notice neither of those two bets appears here, anywhere on the roadmap, not even at the end. They're not "last" — they're out, because they never made it to compete for a spot in the order.

That's, in one sentence, the whole bridge: product-thinking-for-engineers-guide taught you to build the stopwatch (riceScore, prioritize) with precision and honesty. This guide taught you to build the qualifying line (strategicFilter) that decides who has the right to race. No serious product team uses only one of the two — the stopwatch without qualifying rewards whatever's fastest, regardless of whether it belongs in the race; qualifying without a stopwatch tells you who can race, but never in what order.

Deep dive: why the order of the two functions never gets reversed

It's worth asking what would happen if you reversed the order: running prioritize on the complete backlog first, and applying strategicFilter afterward, only on the already-ordered result. Numerically, the final set of eligible bets would be identical —strategicFilter doesn't depend on the order bets arrive in, it just evaluates each one separately. But the process would be worse, in a very concrete way: if the team first sees the complete RICE ranking, with lowestPriceMatch leading by a huge margin, that number has already generated an expectation, a conversation, maybe even a promise to a stakeholder, before anyone asks whether it belongs to the strategy. The filter, applied afterward, would have to reverse a decision that had already started to take shape, instead of preventing it from the start.

This is why lesson 4 insisted so much that the servesDimensions label be decided before calculating RICE, and why this lesson runs strategicFilter as the first step, without exception. It's not just a code-style preference — it's a process discipline: the belonging question has to be asked while there's still no attractive number competing for anyone's attention.

Common mistakes

Confusing the strategic filter with tactical prioritization. What happens: a team treats strategicFilter and prioritize as if they were the same question with two different names, or worse, believes applying RICE already somehow includes the strategic evaluation. Why it happens: both functions end up producing an order or a list, and that superficial similarity of form —both return "the bets that matter"— hides that they answer completely different questions with completely different inputs. How to spot it: ask your team, about any bet on the current roadmap, "did it first go through a strategic-belonging evaluation, or just a RICE calculation?" — if nobody can distinguish the two evaluations, probably only one was done. How to fix it: keep the two steps separate and in this lesson's order, always — filter first, without exception, RICE after, only among those who passed.

Running RICE before the filter "to not waste time." What happens: under time pressure, a team calculates RICE on the complete backlog first —it's faster, they already have the formula automated— intending to "filter later, if needed," and ends up, in practice, never applying the filter again because the RICE ranking already felt like a decision made. Why it happens: calculating RICE is mechanical and fast; evaluating strategic belonging requires judgment, and under pressure, the mechanical always goes first. How to spot it: if your quarterly planning process starts with a RICE spreadsheet and the strategic filter shows up, if at all, as an optional later review, the order is already reversed. How to fix it: make the strategic filter the mandatory first step of the planning process, not a conscience check done "if there's time left" — lesson 4 showed exactly how much reversing the order costs.

Applying the filter once per quarter and never revisiting it when the strategy changes. What happens: the team defines mercadoStrategy once, codes it, and keeps using it unquestioned quarter after quarter, even after module 5 (competition) or module 6 (moats) of this same guide revealed the landscape had changed. Why it happens: once strategicFilter is running and producing consistent results, it feels like a finished piece of infrastructure, not a living hypothesis that needs revisiting. How to spot it: if winOn and avoid haven't changed in over a year, despite the competitive landscape having changed (as you saw in module 5, with competitiveMap's three-year projection), the filter might be running on an obsolete strategy. How to fix it: review mercadoStrategy with the same cadence you review the competitive landscape and moats — the filter is only as good as the strategy feeding it, and a strategy that no longer reflects the real terrain stops protecting against anything.

Exercises

Exercise 1 — Verify the result without running Node. Without running the code, calculate by hand the riceScore of the three eligible bets (recommendations, sellerTools, reviews) and sort them. Does your result match the worked example's output?

See solution

recommendations: (5000 × 1 × 0.5) / 3 = 2500 / 3 = 833.33. sellerTools: (1200 × 2 × 0.8) / 2 = 1920 / 2 = 960. reviews: (6000 × 0.5 × 0.8) / 1 = 2400 / 1 = 2400. Sorted highest to lowest: reviews (2400) > sellerTools (960) > recommendations (833.33) — exactly the order in the worked example's table. The exercise confirms something important: prioritize, applied to the three eligible bets, is the same formula as always, with no special adjustment — the filter doesn't change how RICE is calculated, it only decides which subset it's calculated on.

Exercise 2 — Simulate a capacity change. With the final roadmap (reviews > sellerTools > recommendations, with effort of 1, 2, and 3 respectively) and a capacity of 4 person-months this quarter (less than the 6 from product-thinking-for-engineers-guide), which bets get in, taking them in that order?

See solution

reviews (effort: 1, cumulative 1), sellerTools (effort: 2, cumulative 3), and of recommendations (effort: 3) it would only fit if there were room left — 3 + 3 = 6 > 4, so it doesn't fit in full. With 4 person-months, the team builds reviews and sellerTools, using 3 of the 4 available, and leaves recommendations for the next quarter — not because it doesn't belong to the strategy (it does, it passed the filter), but for a purely tactical capacity reason, exactly the distinction lesson 6 taught you to communicate clearly.

Exercise 3 — Explain the whole bridge to a new engineer on the team. An engineer who just joined Mercado asks: "why don't we just calculate RICE directly on the whole backlog, like the product thinking guide says?" Write, in a paragraph, the answer that connects the two guides.

See solution

A sample answer: "product-thinking-for-engineers-guide teaches you to calculate RICE rigorously, and that formula never changes, not here or anywhere else. What we add in this guide is a mandatory step before that: before calculating RICE on any bet, we ask whether that bet belongs to Mercado's specific strategy — curated discovery and seller trust, not price or generic convenience. If we calculated RICE on the whole backlog without that filter, bets like matching the market's lowest price would come out first, with the best number of all, simply because price levers move a lot of people — but not because they bring us closer to the game we chose to play. The filter decides who competes; RICE, applied afterward, decides in what order those who do belong compete. Both tools are necessary, in that order, never the other way around."

Summary and next step

This lesson connected, with code and no ambiguity, the two guides: strategicFilter decides who competes for priority; prioritize —the same riceScore from product-thinking-for-engineers-guide, with no changes— decides the order, applied exclusively to those who already passed the filter. Mercado's final roadmap —reviews > sellerTools > recommendations— is the result of applying the two tools in the right order, and neither of the two highest-gross-RICE bets (lowestPriceMatch, fasterCheckout) appears anywhere on that roadmap.

Before moving on you should be able to: explain, in one sentence, why the strategic filter and RICE are never applied in reverse order, and describe what's lost if they are.

With this you have the module's six complete pieces. Lesson 8 —the project— brings them together into a single deliverable: the complete filter over Mercado's six-bet backlog, the final sequenced roadmap, and the explicit comparison against what pure RICE would have chosen, closing this guide's complete arc.

Resources

  • Roger Martin, "Decoding the Strategy Choice Cascade" — rogermartin.medium.com/decoding-the-strategy-choice-cascade-475d40555eb1. Martin's complete cascade ends in concrete capabilities and management systems — the same kind of bridge between the choice above (strategy) and the execution below (roadmap, RICE) that this lesson builds in code. In English.
  • Itamar Gilad, "Why You Should Stop Using Product Roadmaps and Try GIST Planning" — itamargilad.medium.com/why-i-stopped-using-product-roadmaps-and-switched-to-gist-planning-3b7f54e271d1. Gilad's GIST framework explicitly separates Goals, where the filter is born, from Steps and concrete tasks, where work gets sequenced — the same layer separation from this lesson, with different vocabulary. In English.
  • Marty Cagan (Silicon Valley Product Group), "Product Strategy" — svpg.com/product-strategy-overview. Cagan describes product strategy's work as the bridge between insight and the team's action — the exact bridge this lesson makes explicit between the two guides. In English.