Módulo 3: The Opportunity Solution Tree
Mini-proyecto: el opportunity solution tree de recomendaciones de Mercado
Descripción
Las siete lecciones de este módulo construyeron el árbol de Mercado de a poco: la forma de cuatro niveles (lección 2), un outcome verificado en la raíz (lección 3), el criterio para distinguir oportunidades reales de soluciones disfrazadas (lección 4), soluciones bien colgadas y sin huérfanas (lección 5), experimentos como menú de candidatos (lección 6), y un criterio para ramificar y podar (lección 7). Este mini-proyecto junta las cuatro piezas —looksLikeSolution, validateTree, pruneTree— en un solo archivo, y les agrega la pieza final que faltaba: una decisión. No solo "el árbol está bien formado", sino "esta semana, el equipo va a atacar esta oportunidad, con esta solución, probada con este experimento primero".
Conexión con el módulo. No hay ningún modelo nuevo en este proyecto — validateTree() y pruneTree() son exactamente las mismas funciones de las lecciones 5 y 7, sin cambios. Lo que agrega el proyecto es el ensamblaje completo, de punta a punta, y la decisión final que cada lección anterior dejó pendiente para la siguiente: qué construir primero, con qué evidencia de respaldo.
Una analogía: el rompecabezas completo, armado por primera vez
Cada lección de este módulo te dio una pieza distinta del rompecabezas: la forma (lección 2), la raíz (lección 3), el filtro de oportunidades (lección 4), las ramas de soluciones (lección 5), las hojas de experimentos (lección 6), la poda (lección 7). Tenerlas todas por separado, cada una verificada en su propia lección, no es lo mismo que verlas juntas, formando la imagen completa. Este proyecto es el momento de poner todas las piezas sobre la mesa a la vez — y, una vez armado el rompecabezas completo, señalar con el dedo exactamente qué parte de la imagen el equipo va a construir primero.
La solución de referencia, verificada
Parte 1 — Ensamblar y validar el árbol completo
Reutilizamos validateTree() exactamente como quedó en la lección 5, sin ningún cambio, y armamos el árbol completo de esta semana: cuatro oportunidades reales, descubiertas en las entrevistas del módulo 2 — descubrimiento de productos (la que sostiene directamente la apuesta recommendations), confianza en vendedores nuevos, carritos olvidados, y comparación de precios —, más el distractor que alguien del equipo de negocio metió en la última reunión de planeación ("agregar un botón de compra en un clic"), y la idea huérfana que llegó por Slack ("rediseñar el logo de Mercado"). A cada oportunidad le agregamos su impact de RICE, heredado de product-thinking-for-engineers.
// Parte 1: el mismo validateTree de la leccion 5, sin ningun cambio.
// Ensamblamos, en un solo archivo, el arbol completo de Mercado con las
// notas reales de esta semana de entrevistas -- incluido el distractor que
// alguien del equipo de negocio metio en la reunion ("agregar un boton...")
// y la idea huerfana que llego por Slack ("rediseñar el logo").
const SOLUTION_VERBS = ['agregar', 'anadir', 'mostrar', 'construir', 'crear', 'rediseñar', 'implementar', 'enviar', 'poner', 'lanzar', 'disenar'];
function looksLikeSolution(text) {
const firstWord = text.trim().split(' ')[0].toLowerCase();
return SOLUTION_VERBS.includes(firstWord);
}
function validateTree(tree) {
const lines = [];
const flags = [];
lines.push('outcome: "' + tree.outcome + '"');
tree.opportunities.forEach((opp) => {
const suspicious = looksLikeSolution(opp.problem);
lines.push('');
lines.push('opportunity: "' + opp.problem + '"' + (suspicious ? ' [SOSPECHOSA]' : ''));
if (suspicious) flags.push('oportunidad "' + opp.problem + '" parece una solucion disfrazada de problema');
opp.solutions.forEach((sol) => {
lines.push(' solution "' + sol.idea + '" -> opportunity "' + opp.problem + '" -> outcome "' + tree.outcome + '": valida');
if (sol.experiments.length === 0) {
lines.push(' (sin experimentos definidos todavia)');
} else {
sol.experiments.forEach((exp) => lines.push(' experiment: "' + exp + '"'));
}
});
});
(tree.orphanSolutionIdeas || []).forEach((idea) => {
lines.push('');
lines.push('solution "' + idea + '" -> (sin oportunidad): HUERFANA');
flags.push('solucion "' + idea + '" no cuelga de ninguna oportunidad real');
});
return { structure: lines.join('\n'), flags };
}
// pruneTree: exactamente la misma funcion de la leccion 7, sin cambios.
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 mercadoTree = {
outcome: '+GMV via conversion del checkout',
opportunities: [
{
problem: 'no descubro productos que me gustarian sin buscarlos por nombre exacto',
impact: 5,
solutions: [
{ idea: 'recomendaciones personalizadas en home y checkout', experiments: ["fake door: boton 'ver mis recomendaciones' en el checkout", 'prototipo clickable con 20 compradores'] },
{ idea: 'categorias curadas a mano por tendencia', experiments: ['prototipo de landing por categoria con 10 compradores'] },
],
},
{
problem: 'no confio en vendedores nuevos sin reseñas',
impact: 3,
solutions: [
{ idea: 'insignia de vendedor verificado en la ficha de producto', experiments: ['prototipo clickable de la ficha con insignia'] },
],
},
{
problem: 'se me olvida el carrito y no vuelvo a completarlo',
impact: 4,
solutions: [
{ idea: 'recordatorio por email del carrito abandonado', experiments: ['wizard-of-oz: enviar el recordatorio a mano a 30 usuarios'] },
],
},
{
problem: 'agregar un boton de compra en un clic',
impact: 2,
solutions: [
{ idea: 'boton de compra en un clic en la ficha de producto', experiments: [] },
],
},
{
problem: 'comparar precios entre productos similares me toma mucho tiempo',
impact: 3,
solutions: [
{ idea: 'tabla comparativa entre productos similares', experiments: ['prototipo clickable de tabla comparativa', 'fake door: boton comparar en la ficha de producto'] },
],
},
],
orphanSolutionIdeas: ['rediseñar el logo de Mercado'],
};
console.log('=== Parte 1: el arbol completo de Mercado, validado ===\n');
const result = validateTree(mercadoTree);
console.log(result.structure);
console.log('\n=== flags ===');
console.log(result.flags.length === 0 ? 'ninguna' : result.flags.map((f) => '- ' + f).join('\n'));
console.log('\n\n=== Parte 2: podado a las 2 oportunidades de mayor impact ===\n');
const pruned = pruneTree(mercadoTree, 2);
pruned.opportunities.forEach((opp) => console.log('impact ' + opp.impact + ' -- "' + opp.problem + '"'));
Qué esperar. Al correr el archivo completo con Node, la salida es exactamente esta:
=== Parte 1: el arbol completo de Mercado, validado ===
outcome: "+GMV via conversion del checkout"
opportunity: "no descubro productos que me gustarian sin buscarlos por nombre exacto"
solution "recomendaciones personalizadas en home y checkout" -> opportunity "no descubro productos que me gustarian sin buscarlos por nombre exacto" -> outcome "+GMV via conversion del checkout": valida
experiment: "fake door: boton 'ver mis recomendaciones' en el checkout"
experiment: "prototipo clickable con 20 compradores"
solution "categorias curadas a mano por tendencia" -> opportunity "no descubro productos que me gustarian sin buscarlos por nombre exacto" -> outcome "+GMV via conversion del checkout": valida
experiment: "prototipo de landing por categoria con 10 compradores"
opportunity: "no confio en vendedores nuevos sin reseñas"
solution "insignia de vendedor verificado en la ficha de producto" -> opportunity "no confio en vendedores nuevos sin reseñas" -> outcome "+GMV via conversion del checkout": valida
experiment: "prototipo clickable de la ficha con insignia"
opportunity: "se me olvida el carrito y no vuelvo a completarlo"
solution "recordatorio por email del carrito abandonado" -> opportunity "se me olvida el carrito y no vuelvo a completarlo" -> outcome "+GMV via conversion del checkout": valida
experiment: "wizard-of-oz: enviar el recordatorio a mano a 30 usuarios"
opportunity: "agregar un boton de compra en un clic" [SOSPECHOSA]
solution "boton de compra en un clic en la ficha de producto" -> opportunity "agregar un boton de compra en un clic" -> outcome "+GMV via conversion del checkout": valida
(sin experimentos definidos todavia)
opportunity: "comparar precios entre productos similares me toma mucho tiempo"
solution "tabla comparativa entre productos similares" -> opportunity "comparar precios entre productos similares me toma mucho tiempo" -> outcome "+GMV via conversion del checkout": valida
experiment: "prototipo clickable de tabla comparativa"
experiment: "fake door: boton comparar en la ficha de producto"
solution "rediseñar el logo de Mercado" -> (sin oportunidad): HUERFANA
=== flags ===
- oportunidad "agregar un boton de compra en un clic" parece una solucion disfrazada de problema
- solucion "rediseñar el logo de Mercado" no cuelga de ninguna oportunidad real
=== Parte 2: podado a las 2 oportunidades de mayor impact ===
impact 5 -- "no descubro productos que me gustarian sin buscarlos por nombre exacto"
impact 4 -- "se me olvida el carrito y no vuelvo a completarlo"
La Parte 1 confirma algo tranquilizador: de las cinco entradas en opportunities, cuatro son oportunidades reales y bien formadas, con soluciones que cuelgan correctamente de ellas. Solo el distractor de la reunión de planeación queda marcado [SOSPECHOSA], y solo la idea de Slack queda HUERFANA — exactamente los dos casos que un equipo atento debería detectar y dejar fuera de la conversación seria de esta semana. La Parte 2 pasa por encima de esa rama sospechosa sin que nadie tenga que discutirla explícitamente: con impact: 2, ni siquiera compite por el top 2 —aunque, como aprendiste en la lección 7, eso es una coincidencia de este ejemplo, no una garantía general; siempre hay que mirar los dos chequeos.
Parte 3 — La decisión: qué rama atacar, con qué experimento primero
Aquí el proyecto deja la aritmética y pide criterio — el mismo tipo de razonamiento que vas a formalizar con rigor en el módulo 4, con pickTest.
El top 2 podado deja dos oportunidades: "no descubro productos que me gustarían..." (impact 5) y "se me olvida el carrito..." (impact 4). La primera gana con el mayor impacto, y no por casualidad — es, literal, la oportunidad que sostiene la apuesta recommendations desde product-thinking-for-engineers, la misma que quedó con tested: false y el confidence de RICE topado en 0.3 al cierre de esa guía. Elegimos esa oportunidad como la prioridad de esta semana.
Dentro de esa oportunidad hay dos soluciones candidatas: "recomendaciones personalizadas en home y checkout" y "categorías curadas a mano por tendencia". Elegimos la primera — no porque la segunda sea mala (sigue en el árbol, disponible si la primera no funciona), sino porque es, específicamente, la apuesta que el equipo ya priorizó con RICE en la guía anterior, con un score de risk × impact más alto que cualquier alternativa sin probar.
Y dentro de esa solución hay dos experimentos candidatos: el fake door en el checkout, y el prototipo clickable con 20 compradores. La decisión de esta semana es correr el fake door primero — es más barato y más rápido de montar que un prototipo clickable completo, y mide una señal de comportamiento real (clics reales, en el flujo real de checkout) en vez de la reacción de un grupo controlado frente a un prototipo. El prototipo clickable queda como el siguiente paso, útil sobre todo si el fake door da una señal ambigua y hace falta observar con más detalle cómo interactúa la gente con la idea, no solo si le interesa.
Esta decisión —barato primero, más caro después, siempre eligiendo el que mejor responde la pregunta específica en juego, no solo el más cómodo— es exactamente el razonamiento que el módulo 4 va a formalizar con un algoritmo completo: pickTest(assumption, candidateTests).
Errores comunes
Tratar el árbol de hoy como un entregable final, cerrado. Qué pasa: después de correr validateTree() y pruneTree() una vez, el equipo archiva el resultado como si fuera la versión definitiva del árbol, sin planear ninguna actualización futura. Por qué pasa: un árbol validado y podado, con una decisión clara al final, se siente como un trabajo terminado — y en cierto sentido lo está, para esta semana. Cómo detectarlo: pregunta cuándo está planeada la próxima revisión del árbol — si la respuesta es "no lo sé" o "cuando haga falta", el árbol probablemente va a quedar congelado. Cómo corregirlo: recuerda la lección 1 — el árbol es un artefacto vivo. La próxima ronda de entrevistas (módulo 2) puede traer una oportunidad nueva; el resultado del experimento elegido hoy (módulos 4 y 7) va a mover la prioridad de las ramas. Planea revisarlo, no solo construirlo una vez.
Saltarse validateTree() y pruneTree(), y decidir directo por instinto. Qué pasa: alguien del equipo, con buena intuición de producto, propone directamente "ataquemos recomendaciones esta semana" sin pasar por el ensamblaje ni la validación completa del árbol. Por qué pasa: cuando la respuesta "correcta" parece obvia de antemano, correr el proceso completo se siente como un trámite innecesario. Cómo detectarlo: la decisión del equipo coincide, por casualidad, con la que hubiera salido del proceso completo — pero nadie puede explicar, con el árbol delante, por qué esa era la elección correcta y no otra. Cómo corregirlo: aunque la intuición acierte casi siempre, el valor del proceso no es solo llegar a la respuesta correcta — es poder defenderla, y poder detectar los casos (como el distractor de esta lección) donde la intuición se hubiera equivocado sin el chequeo explícito.
Confundir "elegimos el fake door" con "ya diseñamos el experimento". Qué pasa: después de la decisión de la Parte 3, el equipo trata el fake door como si ya estuviera completamente especificado —listo para construirse— cuando en realidad solo se decidió qué tipo de experimento correr, no los detalles de su diseño. Por qué pasa: la Parte 3 de este proyecto se siente como el final de un proceso, y es fácil olvidar que en realidad es el punto de partida del siguiente módulo. Cómo detectarlo: si le preguntas al equipo cuál es la hipótesis exacta que el fake door va a probar, o qué tasa de clics contaría como señal suficiente, la respuesta no existe todavía. Cómo corregirlo: la elección de esta lección —"fake door, no prototipo, para esta solución"— es la entrada que el módulo 4 necesita para diseñar el experimento con rigor: convertir la suposición en una hipótesis falsable, y definir con precisión qué resultado cuenta como evidencia.
Ejercicios
Ejercicio 1 — Agrega una oportunidad nueva de esta semana. Imagina que una nueva ronda de entrevistas (módulo 2) trajo esta nota: "no sé si un producto todavía está disponible cuando lo agrego al carrito". Aplica looksLikeSolution() a mano para confirmar si es una oportunidad válida, y si lo es, agrégala al árbol con al menos una solución y un experimento candidato, en el formato correcto.
Ver solución
La nota no empieza con ninguno de los verbos de SOLUTION_VERBS — pasa el chequeo, es una oportunidad válida. Una entrada razonable para el árbol:
{
problem: 'no se si un producto todavia esta disponible cuando lo agrego al carrito',
impact: 4,
solutions: [
{
idea: 'validar stock en tiempo real al agregar al carrito',
experiments: ['prototipo clickable que simula un aviso de "ultimas unidades" al agregar al carrito'],
},
],
}
Con un impact: 4 razonable, esta oportunidad nueva empataría con "se me olvida el carrito..." por el segundo lugar del top 2 podado — un buen ejercicio de cómo una entrevista nueva puede, literalmente, cambiar la prioridad de la semana siguiente.
Ejercicio 2 — Recalcula la poda con las cinco oportunidades reales. Sin ejecutar Node, si pruneTree(mercadoTree, 4) en vez de 2 — quedándote con las cuatro oportunidades de mayor impacto—, ¿cuáles cuatro sobrevivirían, y cuál de las cinco entradas originales (contando la sospechosa) quedaría afuera?
Ver solución
Ordenando las cinco por impact de mayor a menor: 5 (descubrimiento de productos), 4 (carritos olvidados), 3 (confianza en vendedores), 3 (comparación de precios), 2 (el distractor "agregar un botón..."). El top 4 se queda con las primeras cuatro —exactamente las cuatro oportunidades reales del árbol—, y la única que queda afuera es, precisamente, la sospechosa. Este resultado no es casualidad de este ejercicio en particular: refuerza la advertencia de la lección 7 de que el impacto bajo del distractor es una coincidencia útil de este caso, no una garantía general — en un árbol distinto, una oportunidad disfrazada podría tener impacto alto y sobrevivir la poda sin el chequeo de validateTree().
Ejercicio 3 — Escribe el mensaje de cierre del sprint. En 4-5 frases, y dirigido al resto del equipo de Mercado, escribe el mensaje que resume la decisión de esta lección: qué oportunidad se eligió, qué solución, qué experimento se corre primero, y por qué.
Ver solución
Un mensaje razonable: "Armamos el árbol completo de discovery con las cinco notas de esta semana. Dos no pasaron el chequeo —un botón de compra en un clic resultó ser una solución disfrazada de oportunidad, y rediseñar el logo llegó sin ninguna oportunidad real detrás—, así que las dejamos fuera de la conversación de esta semana. De las cuatro oportunidades reales, la de mayor impacto sigue siendo que los compradores no descubren productos que les gustarían sin buscarlos por nombre exacto — la misma que sostiene nuestra apuesta de recomendaciones. Vamos a atacarla con la solución de recomendaciones personalizadas (no con categorías curadas, que queda como plan B), y el primer experimento va a ser un fake door en el checkout — más barato y más rápido que el prototipo clickable, y con una señal de comportamiento real. El diseño detallado de ese experimento —qué contaría como evidencia suficiente— es el siguiente paso." El mensaje funciona porque no oculta las dos ramas descartadas — las nombra explícitamente, con la razón por la que quedaron fuera.
Resumen y siguiente paso
En este mini-proyecto ensamblaste, de punta a punta, el opportunity solution tree completo de Mercado: cinco entradas en el nivel de oportunidad, de las cuales cuatro pasaron los dos chequeos de validateTree() y una quedó marcada [SOSPECHOSA]; una idea huérfana detectada y dejada fuera; y, con pruneTree(), un top 2 por impact que confirmó lo que ya sospechabas desde product-thinking-for-engineers — la oportunidad de descubrimiento de productos, la que sostiene recommendations, es la prioridad de esta semana. Y tomaste la decisión que cierra el módulo: atacar esa oportunidad con la solución de recomendaciones, probada primero con un fake door en el checkout.
Con esto cierras el módulo 3. Tienes ahora un árbol completo, validado y podado, con una rama específica elegida y un tipo de experimento decidido — pero todavía no diseñado con rigor. Ese diseño —convertir "los usuarios van a comprar más si ven recomendaciones personalizadas" en una hipótesis falsable, y elegir con un algoritmo el test más barato que de verdad la puede refutar entre varios candidatos— es exactamente el trabajo del módulo 4, que retoma justo donde termina esta lección: con una solución y un tipo de experimento ya elegidos, listos para diseñarse en serio.
Recursos
- Teresa Torres, Continuous Discovery Habits — producttalk.org/continuous-discovery-habits. Revísalo como cierre del módulo: el libro completo sobre cómo un equipo mantiene, semana tras semana, exactamente el tipo de árbol vivo que armaste hoy. En inglés.
- Product Talk, "Opportunity Solution Trees: Visualize Your Discovery to Stay Aligned and Drive Outcomes" — producttalk.org/opportunity-solution-trees. El artículo de referencia del marco completo — vale la pena releerlo ahora que tienes tu propio árbol de Mercado armado de punta a punta. En inglés.
- Marty Cagan (SVPG), "Discovery — Problem vs. Solution" — svpg.com/discovery-problem-vs-solution. Como puente hacia el módulo 4: la separación entre problema y solución que sostuvo todo este módulo es, exactamente, la base sobre la que se diseña un experimento que refuta de verdad una suposición. En inglés.