Módulo 3: The Opportunity Solution Tree

De la oportunidad a las soluciones

Descripción

La lección 4 te dio el criterio para reconocer una oportunidad real. Esta lección hace el siguiente paso: colgar soluciones debajo de cada oportunidad — varias, si es posible, porque un problema real casi siempre admite más de una forma de atacarlo — y verificar que cada solución de verdad cuelgue de una oportunidad, no de la nada.

Esa última idea tiene nombre propio en este módulo: una solución huérfana es una idea de producto que alguien propuso sin que exista, detrás, una oportunidad real y verificada. "Rediseñar el logo de Mercado" puede ser una idea perfectamente razonable en algún contexto — pero si nadie puede señalar qué necesidad real de qué usuario resuelve, no tiene ningún lugar en este árbol. No es que la idea esté prohibida; es que, mientras siga huérfana, no hay ninguna forma de justificar por qué el equipo debería invertir tiempo en ella antes que en cualquier otra cosa.

Hoy combinas dos chequeos en un solo modelo, validateTree(): el de la lección 4 (¿esta oportunidad es un problema real, o una solución disfrazada?) y uno nuevo (¿esta solución cuelga de una oportunidad real, o es huérfana?). Juntos, son el filtro completo que separa un árbol bien construido de una lista de buenas intenciones sin estructura.

Conexión con el módulo. validateTree() reutiliza, sin ningún cambio, looksLikeSolution() de la lección 4 — la misma función, la misma lista de verbos. Lo que agrega hoy es la estructura completa del árbol (con soluciones y experimentos colgando de cada oportunidad) y el segundo chequeo, el de las huérfanas. Este es el modelo que vas a reutilizar, sin cambios, en las lecciones 6 y 7, y que vas a correr una última vez, extendido, en el proyecto de la lección 8.

Una analogía cotidiana: el pizarrón de brainstorming

Piensa en una sesión de brainstorming bien llevada. Alguien escribe una pregunta grande en la parte de arriba del pizarrón — "¿cómo hacemos que los usuarios vuelvan más seguido?" —, y debajo de esa pregunta, el equipo pega post-its con ideas: cada post-it es una posible respuesta, y todos cuelgan, visiblemente, de la pregunta de arriba. Cualquiera que llegue tarde a la reunión puede mirar el pizarrón y entender de inmediato qué problema está intentando resolver cada idea.

Ahora imagina que, en medio de la sesión, alguien pega un post-it suelto en una esquina vacía de la pared — una idea que se le ocurrió, sin conectarla con ninguna de las preguntas del pizarrón. Ese post-it puede ser una idea excelente. Pero, tal como está pegado, nadie sabe qué problema resuelve, ni por qué debería competir por la atención del equipo frente a las ideas que sí están conectadas a una pregunta real. Ese post-it suelto es, literal, una solución huérfana: una idea sin oportunidad, flotando en la pared sin ningún hilo que la conecte con una necesidad verificada.

Ejemplo trabajado: validateTree() sobre el árbol de Mercado

Armamos el árbol más completo que has visto hasta ahora: cuatro entradas en opportunities —tres oportunidades reales (descubrimiento de productos, confianza en vendedores nuevos, carritos olvidados) y una disfrazada, colada por alguien del equipo de negocio ("agregar un botón de compra en un clic")— y una lista aparte, orphanSolutionIdeas, con una idea que llegó por Slack sin ninguna oportunidad detrás ("rediseñar el logo de Mercado"). validateTree() recorre todo el árbol, imprime la estructura completa, y junta una lista de flags con cada ruptura que encuentra.

// validateTree: revisa un opportunity solution tree completo.
// Chequea 2 cosas: (1) que cada oportunidad sea un problema, no una solucion
// disfrazada (heuristica: no debe empezar con un verbo de accion tipico de
// una feature -- reutiliza looksLikeSolution de la leccion 4, sin cambios);
// (2) que cada solucion cuelgue de una oportunidad real, no de la nada
// (orphanSolutionIdeas). Es un modelo pedagogico, no NLP real.
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 };
}

const mercadoTree = {
  outcome: '+GMV via conversion del checkout',
  opportunities: [
    {
      problem: 'no descubro productos que me gustarian sin buscarlos por nombre exacto',
      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',
      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',
      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',
      solutions: [
        {
          idea: 'boton de compra en un clic en la ficha de producto',
          experiments: [],
        },
      ],
    },
  ],
  orphanSolutionIdeas: ['rediseñar el logo de Mercado'],
};

console.log('=== validateTree sobre el arbol de Mercado ===\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'));

Qué esperar. Al correr el archivo con Node, la salida es exactamente esta:

=== validateTree sobre el arbol de Mercado ===

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)

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

Fíjate en algo importante, que no salta a la vista a la primera lectura: la línea de la solución "boton de compra en un clic..." también dice "valida", aunque su oportunidad esté marcada [SOSPECHOSA]. Esto no es un error del modelo — es una distinción real que vale la pena entender bien. validateTree() hace dos chequeos independientes: uno estructural (¿esta solución está anidada bajo un objeto de oportunidad, en vez de estar suelta en orphanSolutionIdeas?) y uno de contenido (¿el texto de esa oportunidad se lee como un problema real, o como una solución con otro nombre?). La solución del botón de compra pasa el primer chequeo —técnicamente cuelga de algo— pero ese algo falla el segundo chequeo. Es exactamente el equivalente del post-it pegado debajo de una pregunta que, mirada de cerca, en realidad no es una pregunta —es otra respuesta disfrazada de pregunta—. La solución no está huérfana en sentido estricto, pero toda la rama entera descansa sobre una base falsa.

Compárala con "rediseñar el logo de Mercado", la única entrada en orphanSolutionIdeas: esa sí falla el primer chequeo — no hay ningún objeto de oportunidad, ni siquiera uno sospechoso, del cual cuelgue. Dos rupturas distintas, dos formas distintas de que una solución no merezca todavía un lugar en el árbol.

Por qué la oportunidad de "no descubro productos" tiene dos soluciones, no una

Fíjate también en la primera oportunidad del árbol: tiene dos soluciones colgando — recomendaciones personalizadas y categorías curadas a mano. Esto no es relleno — es, literal, la prueba de que la nota pasó bien la prueba de la lección 4: si de verdad admite varias formas distintas de atacarla, el árbol debería reflejarlo con varias ramas, no con una sola. El resto de las oportunidades de este ejemplo tienen una sola solución cada una, simplemente porque todavía no se exploraron alternativas — no porque no existan. Explorar más de una solución por oportunidad, antes de comprometerse con la primera idea que se le ocurrió a alguien, es una de las razones por las que este árbol existe.

Errores comunes

Empezar por la solución, y bajarle una oportunidad para justificarla después. Qué pasa: alguien ya decidió construir "recomendaciones" —quizás porque lo vio en un competidor, o porque le entusiasma la idea técnica— y solo después busca, hacia atrás, alguna nota de entrevista que sirva de excusa para que la solución "parezca" tener una oportunidad real detrás. Por qué pasa: cuando la solución ya está decidida, encontrar alguna justificación es fácil — casi cualquier problema vago puede estirarse para que "encaje" con la solución que ya se quería construir de todas formas. Cómo detectarlo: pregúntate si la oportunidad se escribió antes o después de decidir la solución — si el orden real fue solución primero, oportunidad después, es una señal de alarma, aunque el árbol termine viéndose bien formado. Cómo corregirlo: el orden correcto es siempre oportunidad primero (validada en entrevistas reales, módulo 2), solución después. Si te sorprendes buscando una oportunidad para justificar una solución que ya elegiste, vuelve a la lección 4 y aplica el criterio sin sesgo.

Conformarse con una sola solución por oportunidad, sin buscar alternativas. Qué pasa: cada oportunidad del árbol tiene exactamente una solución, la primera que se le ocurrió a alguien en la reunión — nunca dos o tres opciones compitiendo por la misma oportunidad. Por qué pasa: generar una segunda o tercera alternativa a una idea que ya suena razonable requiere esfuerzo extra, y es tentador declarar el problema "resuelto" en cuanto aparece la primera solución plausible. Cómo detectarlo: revisa el árbol completo — si cada oportunidad tiene exactamente un hijo, sin excepción, es probable que el equipo se haya conformado con la primera idea en vez de comparar varias. Cómo corregirlo: para cada oportunidad nueva, antes de comprometerte con una solución, fuerza al equipo a proponer al menos dos ideas distintas — como la primera oportunidad del ejemplo de hoy, con recomendaciones y categorías curadas compitiendo por la misma necesidad.

Tratar "la solución está anidada" como sinónimo de "la solución es válida". Qué pasa: alguien mira el árbol, ve que "botón de compra en un clic" cuelga de algo (no está en orphanSolutionIdeas), y da por bueno todo ese branch sin fijarse en que la oportunidad de la que cuelga está marcada [SOSPECHOSA]. Por qué pasa: es fácil confundir "tiene una línea que la conecta con algo de arriba" con "esa conexión de verdad significa algo" — el chequeo estructural (¿está anidada?) es mucho más fácil de verificar de un vistazo que el chequeo de contenido (¿la oportunidad de arriba es real?). Cómo detectarlo: usa exactamente el ejemplo de hoy como plantilla — busca ramas donde la oportunidad tenga la marca [SOSPECHOSA], no solo ramas que aparezcan en orphanSolutionIdeas. Cómo corregirlo: recuerda que validateTree() hace dos chequeos independientes — revisa siempre los dos, nunca solo el de la posición estructural.

Ejercicios

Ejercicio 1 — Agrega una segunda solución. Sin ejecutar Node, para la oportunidad "se me olvida el carrito y no vuelvo a completarlo" (que hoy tiene una sola solución, el recordatorio por email), propón una segunda solución distinta que también podría atacar esa misma oportunidad, con al menos un experimento candidato para probarla.

Ver solución

Una propuesta razonable: { idea: 'guardar el carrito automaticamente y mostrarlo apenas el usuario vuelve a abrir la app', experiments: ['prototipo clickable que simula el carrito guardado al reabrir la app'] }. A diferencia del recordatorio por email (que es una acción activa, saliente, hacia el usuario), esta solución ataca la misma oportunidad de forma pasiva — el carrito simplemente está ahí, listo, la próxima vez que el usuario vuelve. Tener dos soluciones distintas para la misma oportunidad permite comparar, más adelante (módulo 4), cuál conviene probar primero — algo que una sola solución nunca hubiera permitido.

Ejercicio 2 — Encuentra la oportunidad real detrás de una huérfana. Para "rediseñar el logo de Mercado" (la solución huérfana del ejemplo de hoy), imagina que alguien defiende la idea diciendo "los usuarios nuevos no confían en la marca". ¿Esa frase es una oportunidad válida, según el criterio de la lección 4? Si lo es, ¿"rediseñar el logo" sigue siendo la única solución razonable, o hay otras?

Ver solución

"Los usuarios nuevos no confían en la marca" sí pasa el criterio de la lección 4 — es una carencia real ("no confían"), no una instrucción de qué construir, y admite varias formas de atacarla. Pero fíjate en algo importante: aunque la oportunidad sea válida, "rediseñar el logo" sigue siendo solo una de varias soluciones posibles — también podrían atacarla con más reseñas visibles, con un sello de "vendedor verificado" (que, de hecho, ya está en el árbol de hoy como solución de otra oportunidad, la de confianza en vendedores), con testimonios de compradores reales, o con una página "sobre nosotros" más completa. El ejercicio muestra algo clave: encontrarle una oportunidad real a una solución huérfana no significa automáticamente que esa solución sea la mejor forma de atacarla — solo le da, por primera vez, un lugar legítimo en el árbol, compitiendo con otras ideas.

Ejercicio 3 — Diseña tu propio árbol de tres niveles. Para la oportunidad "comparar precios entre productos similares me toma mucho tiempo" (todavía no está en el árbol de este ejemplo), escribe el objeto completo con al menos una solución y al menos un experimento — en el mismo formato que usa validateTree() — y verifica a mano que pasaría los dos chequeos sin ningún flag.

Ver solución

Una respuesta razonable:

{
  problem: 'comparar precios entre productos similares me toma mucho tiempo',
  solutions: [
    {
      idea: 'tabla comparativa entre productos similares',
      experiments: ['prototipo clickable de tabla comparativa', 'fake door: boton comparar en la ficha de producto'],
    },
  ],
}

Verificación a mano: el problem no empieza con ninguno de los verbos de SOLUTION_VERBS ("comparar" no está en la lista) — pasa el primer chequeo, no está marcada [SOSPECHOSA]. La solución está anidada dentro de opportunities, no en orphanSolutionIdeas — pasa el segundo chequeo, no aparece como HUERFANA. La lección 6 va a agregar exactamente esta oportunidad al árbol completo de Mercado, así que puedes verificar tu respuesta comparándola ahí.

Resumen y siguiente paso

En esta lección combinaste dos chequeos en un solo modelo, validateTree(): ¿la oportunidad es un problema real, no una solución disfrazada? (reutilizando looksLikeSolution() de la lección 4, sin cambios) y ¿la solución cuelga de una oportunidad real, no de la nada? Corriste el modelo sobre el árbol de Mercado con cuatro oportunidades y una idea huérfana, y viste cómo distingue una rama completamente inválida ("rediseñar el logo", sin ninguna oportunidad) de una rama estructuralmente anidada pero con una base sospechosa ("botón de compra en un clic", colgando de una oportunidad disfrazada).

Antes de avanzar deberías poder: explicar la diferencia entre una solución huérfana y una solución anidada bajo una oportunidad sospechosa; proponer una segunda solución distinta para una oportunidad que hoy tiene solo una; y escribir, en el formato correcto, una nueva rama de tres niveles (oportunidad, solución, experimento) que pase los dos chequeos de validateTree().

La lección 6 baja hasta el último nivel del árbol — los experimentos— y traza la frontera exacta con los módulos 4 y 5: qué información va en un nodo de experimento hoy, y qué se queda para cuando diseñes el test con rigor.

Recursos

  • Product Talk, "Opportunity Solution Trees: Visualize Your Discovery to Stay Aligned and Drive Outcomes" — producttalk.org/opportunity-solution-trees. Vuelve a este artículo para ver ejemplos visuales de árboles con varias soluciones compitiendo por la misma oportunidad — exactamente el patrón de branching de esta lección. En inglés.
  • Product Talk, "Sourcing Opportunities: Unlocking the Power of Opportunity Mapping" — producttalk.org/sourcing-opportunities. Sobre de dónde salen las oportunidades reales que sostienen un árbol, y por qué una solución sin ese origen queda huérfana. En inglés.
  • Marty Cagan (SVPG), "Discovery — Problem vs. Solution" — svpg.com/discovery-problem-vs-solution. Ya lo viste en la lección 4; vale la pena releerlo aquí con el árbol completo en la cabeza — la separación entre espacio de problema y espacio de solución es, literal, la estructura de dos niveles que conectaste hoy. En inglés.