Module 7: Strategy To Roadmap

High RICE, out of strategy

Description

The previous lesson already showed the pattern once: the two bets with the highest riceScore in Mercado's entire backlog don't pass the strategic filter. This lesson stops there, fully, because it's the most expensive —and easiest to make— mistake in the whole module: letting yourself be convinced by a big number and skipping the belonging question. A high riceScore isn't evidence that a bet deserves to be built first. It's evidence that, if it belonged to the strategy, it would deserve to be built first. The condition never stops applying, and this lesson teaches you not to forget it right when the number is most tempting.

Connection to the module. This lesson introduces no new mechanism — it takes strategicFilter, as completed in lesson 3, and puts it head-to-head against what a team would do if it only looked at RICE, without the filter. The direct comparison, ranking versus ranking, is the clearest way to see how much the decision changes when the filter runs first.

An everyday analogy: the bargain you don't need

You walk into a store specifically looking for a new coffee maker. On the way to the appliances section, you pass a table with a huge sign: "70% off — today only" over an electric grill. The discount is real. The price is, objectively, an extraordinary bargain — probably the best price per unit of value in the whole store that day. And yet, if you have no yard, no terrace, no place to use a grill, buying it doesn't save you money: it makes you spend it on something you weren't going to buy anyway, just because the discount was too good to ignore.

That's exactly what happens with a bet that has a high riceScore but is out of strategy. The number —like the discount— is real, and objectively attractive compared to the rest of the available options. But "attractive in the abstract" and "something you need, given what you came to do" are completely different questions. The discounted grill never should have competed for your attention — not because it's a bad product, but because it wasn't on your list, no matter how good the deal was. lowestPriceMatch, with the best riceScore in Mercado's entire backlog, is exactly that grill: a real bargain, for a store that isn't Mercado's.

Worked example: what RICE alone would have chosen first

We run Mercado's complete backlog twice: first, sorted by RICE alone, as a team that never applied the strategic filter would do —exactly the result you'd get by following, with no additional step, product-thinking-for-engineers-guide's method—; then, the same backlog run through strategicFilter, to compare the top two of each ranking, side by side.

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

function prioritize(items) {
  return items
    .map((item) => ({ feature: item.feature, riceScore: Number(riceScore(item.rice).toFixed(2)) }))
    .sort((a, b) => b.riceScore - a.riceScore);
}

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)),
    };
  });
}

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('=== If you prioritized by RICE ALONE (no strategic filter) ===\n');
console.table(prioritize(backlog));

console.log('\n=== The same backlog, run through strategicFilter BEFORE looking at riceScore ===\n');
const filtered = strategicFilter(backlog, mercadoStrategy).sort((a, b) => b.riceScore - a.riceScore);
console.table(filtered.map((b) => ({ feature: b.feature, riceScore: b.riceScore, inStrategy: b.inStrategy })));

const top2Naive = prioritize(backlog).slice(0, 2).map((b) => b.feature);
console.log('\nTop 2 by-RICE-alone: ' + top2Naive.join(' and ') + '.');
console.log('Top 2 with the strategic filter applied first: ' + filtered.filter((b) => b.inStrategy).slice(0, 2).map((b) => b.feature).join(' and ') + '.');

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

=== If you prioritized by RICE ALONE (no strategic filter) ===

┌─────────┬────────────────────┬───────────┐
│ (index) │      feature       │ riceScore │
├─────────┼────────────────────┼───────────┤
│    0    │ 'lowestPriceMatch' │   7600    │
│    1    │  'fasterCheckout'  │   6400    │
│    2    │     'reviews'      │   2400    │
│    3    │  'improvedSearch'  │   1500    │
│    4    │   'sellerTools'    │    960    │
│    5    │ 'recommendations'  │  833.33   │
└─────────┴────────────────────┴───────────┘

=== The same backlog, run through strategicFilter BEFORE looking at riceScore ===

┌─────────┬────────────────────┬───────────┬────────────┐
│ (index) │      feature       │ riceScore │ inStrategy │
├─────────┼────────────────────┼───────────┼────────────┤
│    0    │ 'lowestPriceMatch' │   7600    │   false    │
│    1    │  'fasterCheckout'  │   6400    │   false    │
│    2    │     'reviews'      │   2400    │    true    │
│    3    │  'improvedSearch'  │   1500    │   false    │
│    4    │   'sellerTools'    │    960    │    true    │
│    5    │ 'recommendations'  │  833.33   │    true    │
└─────────┴────────────────────┴───────────┴────────────┘

Top 2 by-RICE-alone: lowestPriceMatch and fasterCheckout.
Top 2 with the strategic filter applied first: reviews and sellerTools.

Compare the first two rows of each table, because that's where the full cost of skipping the filter lives. A team that only looks at RICE builds, first, lowestPriceMatch and then fasterCheckout — two bets that, together, consume the quarter's most valuable capacity without reinforcing curated discovery or seller trust even once, the two dimensions Mercado, across five modules of work, decided were its game. A team that applies the filter first builds reviews and sellerTools — bets with a considerably lower riceScore in absolute terms, but that are, both, direct reinforcements of the strategy. The difference between the two teams isn't in the RICE calculation —it's identical in both cases, the same formula, the same numbers. It's in when the belonging question gets asked: before looking at the number, or never.

Deep dive: why the large numerator is precisely what deceives

It's worth understanding why out-of-strategy bets tend to have a high riceScore, instead of assuming it's a coincidence of this example. lowestPriceMatch has reach: 9500 and impact: 3 (the scale's maximum level) because almost everyone responds to a lower price — it's, almost by definition, the widest-reach, highest-gross-impact lever that exists in any consumer business. That's not an accident or a manipulation of the numbers: it's the exact reason so many teams, with no explicit strategy to stop them, end up competing on price sooner or later. Price is the easiest bet to justify with pure RICE, precisely because it moves more people, faster, with more certainty, than almost any more specialized alternative.

That's the whole trap, in one sentence: the most generic bets —the ones that serve any business, not this particular business— tend to win on pure RICE, because RICE measures aggregate reach and impact, not fit with a specific strategy. A real strategy, by definition, chooses a narrow game —a segment, a differentiation, a small set of dimensions to bet heavily on— and that narrowing almost always sacrifices gross reach in exchange for depth in one specific place. recommendations, the bet with the lowest riceScore in the whole backlog, serves exactly Mercado's explorers, not any buyer of any store — and that's why its reach is smaller, not because it's a weak bet.

Common mistakes

Getting dazzled by the large numerator without asking where it comes from. What happens: upon seeing an unusually high riceScore, the team interprets it as a sign the bet is exceptionally good, without asking whether that large number comes precisely from being a generic bet that serves any business. Why it happens: a large number feels like objective validation —harder to question in a meeting than a qualitative argument about strategy. How to spot it: if your backlog's highest-riceScore bet would serve a competitor with a completely different strategy from yours exactly as well, be suspicious — as you saw in this lesson, lowestPriceMatch would work for any marketplace, not specifically for the one Mercado chose to be. How to fix it: before celebrating a high riceScore, run strategicFilter and ask which dimension of your particular strategy it reinforces — if the answer is "none in particular, it would help anyone," that's exactly this lesson's pattern.

Rationalizing the trap after seeing it, instead of accepting the filter's result. What happens: when the filter flags a popular, high-RICE bet as out of strategy, someone on the team tries to retroactively relabel its servesDimensions so it passes —lesson 2's mistake in reverse, now applied with the full pressure of an already-known number that's already generated excitement. Why it happens: it's more uncomfortable to say no to a bet the team has already seen with an attractive number, than to say no before calculating anything. How to spot it: if a bet's servesDimensions label changes after seeing its riceScore, and not before, that relabeling is suspect almost by definition. How to fix it: always label servesDimensions before calculating RICE — the order of operations isn't a cosmetic detail, it's this whole module's discipline; if the label gets decided after seeing the number, the filter stops protecting against anything.

Assuming a high riceScore already implies, on its own, strategic fit. What happens: riceScore gets treated as a composite measure that would already implicitly include how well the bet fits the strategy —"if it has that much reach and impact, it must be good for us somehow." Why it happens: RICE feels like a complete analysis because it combines four different variables into a single number — that combination can give the false sense it already captured everything that matters. How to spot it: review what each of RICE's four inputs measures (reach, impact, confidence, effort, from product-thinking-for-engineers-guide, module 3) — none of the four asks anything about segment, positioning, differentiation, or moats. How to fix it: treat riceScore and inStrategy as two completely different questions, calculated with completely different inputs, and never let a high value on the first substitute for the answer to the second — exactly as this lesson's comparison table demonstrated.

Exercises

Exercise 1 — Design your own trap. Invent a hypothetical bet for Mercado, different from lowestPriceMatch, that would have a very high riceScore but would fall out in strategicFilter. Write its complete object (feature, servesDimensions, rice) and explain in one sentence why it would have high RICE and why it doesn't belong.

See solution

A reasonable answer: { feature: 'freeDeliveryForEveryone', servesDimensions: ['deliverySpeed'], rice: { reach: 9000, impact: 3, confidence: 0.8, effort: 2 } } — with a riceScore of (9000 × 3 × 0.8) / 2 = 10800, even higher than lowestPriceMatch. It would have very high RICE for the same reason as lowestPriceMatch: free shipping for everyone is a generic lever that moves almost any buyer, regardless of their strategy. It wouldn't belong to Mercado's strategy because deliverySpeed isn't in winOn (['curatedDiscovery', 'sellerTrust']) — it would be, with reinforces: [], exactly the same "neutral" pattern as fasterCheckout in lesson 3, though with an even bigger, even more tempting number.

Exercise 2 — Calculate the opportunity cost in person-months. lowestPriceMatch costs 3 person-months (effort: 3) and fasterCheckout costs 2 — a total of 5 person-months if the team had built both by pure RICE. If the quarter's capacity is 6 person-months (the same limit used in product-thinking-for-engineers-guide, module 3), how much capacity would the team have left to build something that actually reinforces the strategy, after having fallen into the trap?

See solution

Only 1 person-month would remain (6 − 5 = 1), insufficient to build any of the three bets that actually belong to the strategy: reviews (effort: 1) would barely fit, but sellerTools (effort: 2) and recommendations (effort: 3) wouldn't fit anymore. The real cost of the trap isn't just "building something that doesn't help" — it's also consuming the capacity you needed to build what actually mattered, leaving the team with insufficient resources to execute its own strategy that same quarter.

Exercise 3 — Defend lowestPriceMatch as a salesperson would, then respond. A sales colleague argues: "customers ask for a low price all the time, and this bet has the best RICE in the backlog — let's build it." Write, in a paragraph, a response that acknowledges the validity of the customer demand without accepting the conclusion.

See solution

A sample answer: "You're right that customers ask for a low price — almost every customer of any business does, and that's exactly why lowestPriceMatch has the best RICE in the backlog: it's the most universal lever there is. But universal isn't the same as ours. We chose, with effort, not to compete on that dimension —a player at a generic megastore's scale is always going to beat us there—, and instead to compete on curated discovery and seller trust, where we can genuinely win. Building lowestPriceMatch doesn't hurt Mercado in the sense that people would use it — but it consumes the capacity we need to deepen the advantage that actually is ours, and pulls us, without us noticing, into the price-and-scale fight we already decided we can't win."

Summary and next step

This lesson isolated the most expensive pattern in the whole module: the highest-riceScore bets aren't automatically the right bets — they're often exactly the opposite, because the most generic levers (price, shipping, universal convenience) tend to win on gross reach and impact precisely for being generic, not for being aligned with any particular strategy. You saw, with real numbers, that the two highest-RICE bets in Mercado's entire backlog —lowestPriceMatch and fasterCheckout— are the two the strategic filter rejects.

Before moving on you should be able to: explain why generic bets tend to win on pure RICE, and recognize, in your own work, when an attractive number is about to pull you away from a strategy you already chose with effort.

Lesson 5 looks closely at the three bets that did pass the filter —reviews, sellerTools, recommendations— and asks something you haven't answered yet: do these three bets reinforce each other, building an increasingly deep advantage, or do they simply share the coincidence of not having conflicted with avoid?

Resources