Módulo 8: Project Prioritize Mercados Roadmap

Recorta cada apuesta al MVP que prueba su suposición riesgosa

Descripción

Ya sabes cuál es la suposición más riesgosa de cada una de las cinco apuestas (lección 5). La pregunta de esta lección es la del módulo 5, aplicada a las cinco a la vez: ¿cuál es la cosa más pequeña que pone esa suposición específica a prueba, sin construir la apuesta entera? Vas a reutilizar compareApproaches para comparar, apuesta por apuesta, el costo y el momento de la evidencia de construir todo contra construir el MVP — y selectMinimalScope para recortar, feature por feature, el backlog propuesto de una de las cinco (sellerTools) a su núcleo mínimo.

Conexión con el módulo. Esta es la Capa 5 de las siete. Fíjate en un detalle importante de cómo se conecta con la lección anterior: el campo riskiestAssumption de cada apuesta, en el código de hoy, es literalmente el texto que ganó en rankAssumptions en la lección 5 — no una suposición nueva ni genérica. Esto no es casualidad: un MVP sin una suposición riesgosa específica detrás no es un MVP, es solo "algo pequeño y barato" (como advirtió la lección 2 del módulo 5). El resultado de esta lección —el esfuerzo en person-days de cada MVP— es, además, el insumo directo del jobSize que vas a usar en la lección 7 para secuenciar el roadmap.

Ejemplo trabajado: compareApproaches sobre las cinco apuestas

Cada apuesta trae dos planes —construir todo, o el MVP— y su suposición riesgosa, tomada directamente de la lección 5:

// L06: recortamos las 5 apuestas al MVP con compareApproaches (modulo 5),
// reusando la suposicion mas riesgosa de cada una (leccion 5).
function compareApproaches(bet) {
  const { name, riskiestAssumption, buildEverything, mvp } = bet;
  const effortRatio = Math.round((mvp.effort / buildEverything.effort) * 100);
  const sameEvidence = buildEverything.provesAssumption === mvp.provesAssumption;
  return {
    bet: name,
    riskiestAssumption,
    buildEverything: { effort: buildEverything.effort, evidenceAt: buildEverything.evidenceAt },
    mvp: { effort: mvp.effort, evidenceAt: mvp.evidenceAt },
    effortRatio,
    sameEvidence,
  };
}

function summarizeBets(bets) {
  const results = bets.map(compareApproaches);
  const totalBuildEverythingEffort = results.reduce((sum, r) => sum + r.buildEverything.effort, 0);
  const totalMvpEffort = results.reduce((sum, r) => sum + r.mvp.effort, 0);
  const totalSaved = totalBuildEverythingEffort - totalMvpEffort;
  const totalSavedPercent = Math.round((totalSaved / totalBuildEverythingEffort) * 100);
  return { results, totalBuildEverythingEffort, totalMvpEffort, totalSaved, totalSavedPercent };
}

const mvpBacklog = [
  {
    name: 'fasterCheckout',
    // suposicion ganadora de la leccion 5 (riskScore: 0.54)
    riskiestAssumption: 'reducir de 5 a 3 pasos elimina friccion real que causa abandono, no solo pasos cosmeticos',
    buildEverything: { effort: 18, evidenceAt: 'dia 18', provesAssumption: true },
    mvp: { effort: 3, evidenceAt: 'dia 3', provesAssumption: true }, // A/B del flujo de 3 pasos, 10% del trafico mobile web
  },
  {
    name: 'recommendations',
    // suposicion ganadora de la leccion 5 (riskScore: 0.45)
    riskiestAssumption: 'mostrar productos complementarios en el carrito hace que mas compradores agreguen un segundo producto antes de pagar',
    buildEverything: { effort: 40, evidenceAt: 'dia 40', provesAssumption: true },
    mvp: { effort: 4, evidenceAt: 'dia 4', provesAssumption: true }, // tabla fija "comprados juntos", del proyecto del modulo 1
  },
  {
    name: 'sellerTools',
    // suposicion ganadora de la leccion 5 (riskScore: 0.52)
    riskiestAssumption: 'si los vendedores ven que productos rotan lento o se quedan sin stock, ajustan precio o stock a tiempo, y eso reduce las ventas perdidas',
    buildEverything: { effort: 25, evidenceAt: 'dia 25', provesAssumption: true },
    mvp: { effort: 3, evidenceAt: 'dia 3', provesAssumption: true }, // reporte semanal por email a vendedores piloto
  },
  {
    name: 'reviews',
    // suposicion ganadora de la leccion 5 (riskScore: 0.47)
    riskiestAssumption: 'ver una calificacion visible en la pagina del producto aumenta la probabilidad de que un comprador complete la compra',
    buildEverything: { effort: 20, evidenceAt: 'dia 20', provesAssumption: true },
    mvp: { effort: 3, evidenceAt: 'dia 3', provesAssumption: true }, // star rating + conteo, sembrado a mano
  },
  {
    name: 'improvedSearch',
    // suposicion ganadora de la leccion 5 (riskScore: 0.44) -- OJO: es la suposicion sobre
    // el DIAGNOSTICO del problema, no sobre si el algoritmo de correccion funciona.
    riskiestAssumption: 'la mayoria del abandono en busqueda pasa por errores de tipeo o sinonimos, y no por falta de inventario relevante en el catalogo',
    buildEverything: { effort: 35, evidenceAt: 'dia 35', provesAssumption: true },
    mvp: { effort: 5, evidenceAt: 'dia 5', provesAssumption: true }, // etiquetado manual de una muestra de busquedas fallidas
  },
];

console.log('=== compareApproaches sobre las 5 apuestas ===\n');
const summary = summarizeBets(mvpBacklog);
summary.results.forEach((r) => {
  console.log(r.bet + ':');
  console.log('  buildEverything: ' + r.buildEverything.effort + ' person-days, evidencia ' + r.buildEverything.evidenceAt);
  console.log('  mvp: ' + r.mvp.effort + ' person-days, evidencia ' + r.mvp.evidenceAt + '  (ratio: ' + r.effortRatio + '%)\n');
});
console.log('Totales: construir todo = ' + summary.totalBuildEverythingEffort + ' person-days | los 5 MVP = ' +
  summary.totalMvpEffort + ' person-days (' + summary.totalSavedPercent + '% del total)');

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

=== compareApproaches sobre las 5 apuestas ===

fasterCheckout:
  buildEverything: 18 person-days, evidencia dia 18
  mvp: 3 person-days, evidencia dia 3  (ratio: 17%)

recommendations:
  buildEverything: 40 person-days, evidencia dia 40
  mvp: 4 person-days, evidencia dia 4  (ratio: 10%)

sellerTools:
  buildEverything: 25 person-days, evidencia dia 25
  mvp: 3 person-days, evidencia dia 3  (ratio: 12%)

reviews:
  buildEverything: 20 person-days, evidencia dia 20
  mvp: 3 person-days, evidencia dia 3  (ratio: 15%)

improvedSearch:
  buildEverything: 35 person-days, evidencia dia 35
  mvp: 5 person-days, evidencia dia 5  (ratio: 14%)

Totales: construir todo = 138 person-days | los 5 MVP = 18 person-days (87% del total)

Con 18 person-days —menos de un mes de trabajo de una sola persona— el equipo puede tener evidencia real sobre las cinco apuestas del trimestre, contra 138 person-days (casi siete meses-persona) de construirlas todas enteras. Ningún effortRatio individual pasa del 17%. Y fíjate en el MVP de improvedSearch: son 5 person-days, pero no para construir un algoritmo de corrección ortográfica a medias — son para etiquetar a mano una muestra de búsquedas fallidas y medir cuántas de verdad se explican por typos o sinónimos, contra cuántas fallan por falta de inventario. Ese MVP prueba exactamente la suposición riesgosa que ganó en la lección 5 —el diagnóstico del problema—, no la pregunta más fácil y menos importante de si el algoritmo funciona bien.

selectMinimalScope: recortando el backlog de features de sellerTools

El plan completo de sellerTools ("dashboard completo con gráficos en tiempo real, alertas automáticas y exportación") se puede desglosar en seis features candidatas. Aplicamos el algoritmo del módulo 5 para verificar, feature por feature, que el mvp.effort: 3 de arriba no es un número inventado, sino la suma exacta de la única feature que prueba la suposición riesgosa:

function selectMinimalScope(features) {
  const selected = features.filter((f) => f.provesTheBet);
  const cut = features.filter((f) => !f.provesTheBet);
  const totalCost = features.reduce((sum, f) => sum + f.cost, 0);
  const selectedCost = selected.reduce((sum, f) => sum + f.cost, 0);
  const savedCost = totalCost - selectedCost;
  const savedPercent = Math.round((savedCost / totalCost) * 100);
  return { selected: selected.map((f) => f.name), cut: cut.map((f) => f.name), totalCost, selectedCost, savedCost, savedPercent };
}

const sellerToolsFeatures = [
  { name: 'Reporte semanal por email con los 5 productos de peor rotacion, generado a mano con una query', cost: 3, provesTheBet: true },
  { name: 'Graficos en tiempo real de ventas por producto', cost: 8, provesTheBet: false },
  { name: 'Alertas automaticas cuando un producto se queda sin stock', cost: 5, provesTheBet: false },
  { name: 'Exportacion a Excel/CSV de todo el catalogo', cost: 3, provesTheBet: false },
  { name: 'Vista historica de tendencias de rotacion (12 meses)', cost: 4, provesTheBet: false },
  { name: 'Ranking de mejores y peores vendedores del marketplace', cost: 2, provesTheBet: false },
];
const scope = selectMinimalScope(sellerToolsFeatures);
console.log('seleccionadas: ' + scope.selected.join(', '));
console.log('cortadas: ' + scope.cut.join(', '));
console.log('costo total: ' + scope.totalCost + ' | costo del MVP: ' + scope.selectedCost + ' | ahorro: ' + scope.savedCost + ' (' + scope.savedPercent + '%)');

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

seleccionadas: Reporte semanal por email con los 5 productos de peor rotacion, generado a mano con una query
cortadas: Graficos en tiempo real de ventas por producto, Alertas automaticas cuando un producto se queda sin stock, Exportacion a Excel/CSV de todo el catalogo, Vista historica de tendencias de rotacion (12 meses), Ranking de mejores y peores vendedores del marketplace
costo total: 25 | costo del MVP: 3 | ahorro: 22 (88%)

Dos números para verificar la consistencia entre las dos partes de esta lección: costo total: 25 es exactamente el buildEverything.effort: 25 de sellerTools en la tabla de arriba, y costo del MVP: 3 es exactamente su mvp.effort: 3. No es coincidencia — es la misma cifra, calculada de dos formas: arriba, como un dato de entrada ya decidido; aquí, como la suma exacta de la única feature (de seis candidatas) que de verdad prueba si los vendedores reaccionan a ver sus datos de rotación. Los gráficos en tiempo real, las alertas automáticas, la exportación, el histórico de doce meses y el ranking de vendedores —cinco de las seis features, 22 de los 25 person-days del plan completo— se cortan sin perder nada de la evidencia que este trimestre necesita: ninguna de ellas es necesaria para saber si un vendedor, al ver que un producto suyo rota mal, ajusta el precio o el stock a tiempo.

Errores comunes

Diseñar el MVP para la suposición equivocada. Qué pasa: al construir el MVP de improvedSearch, alguien diseña un experimento que mide si el algoritmo de corrección ortográfica funciona técnicamente bien (por ejemplo, "¿detecta el 90% de los typos comunes?"), en vez de medir si el abandono en búsqueda de verdad se explica por typos. Por qué pasa: la pregunta técnica es más fácil de convertir en un experimento de ingeniería tradicional (una suite de test cases) que la pregunta de diagnóstico, que exige mirar datos de comportamiento real. Cómo detectarlo: compara el MVP diseñado contra el texto exacto de la riskiestAssumption de la lección 5 — si no se corresponden palabra por palabra en lo que prueban, el MVP está probando la suposición incorrecta. Cómo corregirlo: como en el ejemplo de hoy, el MVP de improvedSearch no es "un algoritmo a medias" — es un etiquetado manual de búsquedas fallidas reales, exactamente diseñado para responder la pregunta de diagnóstico que ganó en la lección 5.

Recortar features al azar, sin el criterio de provesTheBet. Qué pasa: al recortar el backlog de sellerTools, alguien elige qué cortar por cuáles features parecen "más caras" o "más difíciles", no por cuáles efectivamente prueban la suposición riesgosa. Por qué pasa: cortar por costo es más intuitivo que cortar por evidencia. Cómo detectarlo: si el criterio de corte de tu equipo es "quitemos lo más caro" en vez de "quitemos lo que no prueba la suposición", podrías terminar cortando la única feature que sí importa y dejando cinco que no aportan evidencia. Cómo corregirlo: la pregunta correcta, para cada feature, es siempre la misma: "si esta feature no existiera, ¿podríamos igual concluir algo sobre la suposición riesgosa?" — si la respuesta es sí, se corta, sin importar cuánto cueste.

Sumar el effortRatio de las cinco apuestas y presentarlo como un solo número. Qué pasa: alguien promedia los cinco effortRatio individuales (17%, 10%, 12%, 15%, 14%) y presenta "en promedio, un MVP cuesta 14% de construir todo" como una regla general aplicable a cualquier apuesta futura. Por qué pasa: un solo número resumen es más fácil de repetir que cinco distintos. Cómo detectarlo: se usa esa cifra promedio para estimar el costo de un MVP que todavía no se diseñó. Cómo corregirlo: cada apuesta necesita su propio compareApproaches, con su propio plan completo y su propio MVP diseñado a propósito para su suposición riesgosa específica — el promedio de hoy describe estas cinco apuestas con estos datos, no una ley general.

Ejercicios

Ejercicio 1 — Verifica el ahorro total sin recommendations. Si por alguna razón el equipo decidiera no construir el MVP de recommendations este trimestre (queda para el próximo), recalcula totalBuildEverythingEffort, totalMvpEffort y totalSavedPercent con las cuatro apuestas restantes.

Ver solución

Sin recommendations (buildEverything: 40, mvp: 4): totalBuildEverythingEffort = 138 - 40 = 98; totalMvpEffort = 18 - 4 = 14; totalSaved = 98 - 14 = 84; totalSavedPercent = round((84 / 98) × 100) = round(85.7) = 86. El porcentaje de ahorro casi no cambia (87% con las cinco, 86% con cuatro) — tiene sentido, porque recommendations tiene, de las cinco, un effortRatio individual (10%) cercano al promedio del grupo, así que quitarla no distorsiona mucho el agregado. Sí cambia el jobSize disponible para secuenciar en la lección 7: con cuatro apuestas, el trimestre tiene 14 person-days de MVP para repartir, no 18.

Ejercicio 2 — Diseña el MVP de una sexta apuesta. Para "agregar pago con criptomonedas" (la apuesta del ejercicio 2 de la lección 5, cuya suposición riesgosa ganadora fue sobre si existe demanda real, no sobre la integración técnica), propón un plan de MVP —no el buildEverything— que ponga esa suposición específica a prueba, con su effort estimado en person-days y su evidenceAt.

Ver solución

Un MVP razonable: "agregar la opción 'Pagar con cripto (próximamente)' en el checkout, sin ninguna integración real detrás, que solo registra cuántos compradores hacen clic e, idealmente, a cuáles se les pregunta por qué —una encuesta de una pregunta— antes de mostrarles que todavía no está disponible" (effort: 2 person-days, evidenceAt: 'día 2'). Es una variante de "puerta falsa" (fake door), como la que viste en el módulo 5: no requiere ninguna pasarela de pago real, y responde exactamente la suposición riesgosa —¿existe demanda real?— sin gastar ni un día en la integración técnica, que solo tendría sentido investigar después de confirmar que la demanda existe.

Ejercicio 3 — Defiende el recorte de sellerTools frente al equipo de vendedores. El equipo que propuso sellerTools esperaba un dashboard visual con gráficos en tiempo real, no un reporte semanal por email generado a mano. Escribe, en 3-4 frases, cómo defenderías este recorte frente a ellos, sin sonar como si les estuvieras quitando su idea.

Ver solución

Una respuesta posible: "No estamos descartando el dashboard —estamos posponiendo el 88% de su costo (22 de 25 person-days: los gráficos en tiempo real, las alertas, la exportación, el histórico y el ranking) hasta confirmar la parte que de verdad decide si vale la pena: si un vendedor, al enterarse de que un producto suyo rota mal, realmente ajusta el precio o el stock a tiempo. El reporte semanal por email cuesta 3 person-days y responde exactamente esa pregunta, con vendedores reales, en una semana. Si confirma que sí reaccionan, construimos el dashboard completo con mucha más confianza que si lo hubiéramos hecho a ciegas; si no reaccionan, nos ahorramos 22 person-days en una alerta bonita que nadie iba a usar." El argumento funciona porque no minimiza la visión original — la pospone hasta tener evidencia que la justifique, con el mismo espíritu del proyecto del módulo 5.

Resumen y siguiente paso

En esta lección recortaste las cinco apuestas del trimestre a su MVP —el experimento más barato que prueba, específicamente, la suposición más riesgosa de cada una (lección 5)— con compareApproaches, y verificaste con selectMinimalScope, feature por feature, que el número de sellerTools no era arbitrario. El resultado: 18 person-days de evidencia sobre las cinco apuestas, contra 138 person-days de construirlas todas enteras — un ahorro del 87%, sin perder la evidencia que de verdad importa.

Con el tamaño real de cada apuesta (lección 4) y el esfuerzo de su MVP (esta lección) en la mano, la lección 7 cierra el círculo: secuenciar el trimestre por costo de demora, con wsjf del módulo 7 — y ahí vas a ver, con los números completos, si el orden de RICE de la lección 3 sobrevive a las cuatro capas que se agregaron desde entonces.

Recursos

  • Eric Ries, The Lean Startuptheleanstartup.com. El origen del MVP como experimento que valida una hipótesis específica, no una versión reducida del producto. En inglés.
  • Henrik Kniberg, "Making Sense of MVP" — blog.crisp.se/2016/01/25/henrikkniberg/making-sense-of-mvp. El dibujo del monopatín, aplicado ahora a cinco apuestas del mismo trimestre. En inglés.
  • Basecamp, "Shape Up" — basecamp.com/shapeup. Sobre cómo decidir, con el scope ya recortado, en qué orden entran las apuestas a un ciclo de trabajo real — el puente directo hacia la lección 7. En inglés.