Módulo 1: Understanding Deployment Options

6. Decision Matrix Framework

Descripción

En esta cápsula vas a construir el framework que convierte análisis de trade-offs en una decisión documentada y defendible. La decision matrix no es un ejercicio académico — es la herramienta que produces en el proyecto del módulo y que reutilizas en los Módulos 7 y 8. Al terminar, tendrás un template funcional y sabrás cómo llenarlo para cualquier caso de uso.

Contexto: Las cápsulas anteriores te dieron los inputs: categorías de deployment, trade-offs en 5 dimensiones, comparación serverless vs containers, y cost modeling. Esta cápsula te da la estructura para sintetizar todo en una decisión.


Qué Es una Decision Matrix

Definición

Una decision matrix es una tabla que evalúa opciones contra criterios ponderados, produciendo un score numérico que indica cuál opción se alinea mejor con tus requirements. No reemplaza el juicio — lo estructura.

Decision Matrix = Criterios + Pesos + Evaluación + Score

Criterios: Las dimensiones que importan para tu caso
Pesos:     La importancia relativa de cada criterio (suman 100)
Evaluación: Score de cada opción en cada criterio (1-5)
Score:     Peso × Evaluación, sumado por opción

Por qué funciona para deployment

Sin matrix, las decisiones de deployment son: "mi amigo usa Railway" o "AWS es lo que usan las empresas serias." Con matrix, la decisión es: "dado mis constraints de presupuesto, equipo, y tráfico, Railway tiene un score de 4.2 vs AWS con 3.1 — y puedo explicar por qué."

La matrix es especialmente valiosa cuando:

  • Tienes que justificar la decisión ante un equipo o manager
  • Necesitas re-evaluar cuando cambian las condiciones
  • Quieres documentar el razonamiento para tu yo futuro

Anatomía de una Decision Matrix

Estructura completa

# Decision Matrix: [Nombre del proyecto]

## Contexto
- Proyecto: [Qué app AI]
- Stage: [MVP / Growth / Scale]
- Equipo: [Tamaño y skills]
- Presupuesto: [Rango mensual]
- Timeline: [Urgencia]

## Criterios y Pesos

| # | Criterio | Peso | Justificación del peso |
|---|----------|------|----------------------|
| 1 | Coste mensual | 25 | Presupuesto limitado, <$50/mes |
| 2 | Complejidad operativa | 20 | Equipo de 1, no puedo dedicar >2hrs/semana |
| 3 | Time-to-deploy | 20 | Necesito MVP online esta semana |
| 4 | Escalabilidad | 15 | Crecimiento esperado pero no urgente |
| 5 | Control | 10 | Sin requirements de compliance |
| 6 | AI-specific (cold starts, streaming) | 10 | Chat con streaming es feature clave |
| **Total** | | **100** | |

## Evaluación (1-5, donde 5 = mejor)

| Criterio (Peso) | Local | Serverless | Managed | Self-hosted |
|-----------------|:-----:|:----------:|:-------:|:-----------:|
| Coste (25) | 4 | 5 | 4 | 1 |
| Complejidad (20) | 3 | 4 | 5 | 1 |
| Time-to-deploy (20) | 3 | 4 | 5 | 1 |
| Escalabilidad (15) | 2 | 5 | 3 | 4 |
| Control (10) | 4 | 2 | 3 | 5 |
| AI-specific (10) | 5 | 2 | 4 | 5 |

## Scores Ponderados

| Criterio | Local | Serverless | Managed | Self-hosted |
|----------|:-----:|:----------:|:-------:|:-----------:|
| Coste | 100 | 125 | 100 | 25 |
| Complejidad | 60 | 80 | 100 | 20 |
| Time-to-deploy | 60 | 80 | 100 | 20 |
| Escalabilidad | 30 | 75 | 45 | 60 |
| Control | 40 | 20 | 30 | 50 |
| AI-specific | 50 | 20 | 40 | 50 |
| **TOTAL** | **340** | **400** | **415** | **225** |

## Decisión
- **Recomendación:** Managed (Railway) — Score 415
- **Segunda opción:** Serverless (Lambda) — Score 400
- **Descartados:** Self-hosted (Score 225, overkill para el caso)

## Condiciones de re-evaluación
- Si tráfico > 500 req/hora → re-evaluar serverless vs managed
- Si necesito streaming → confirmar que managed lo soporta bien
- Si presupuesto > $200/mes → considerar local (VPS) para más control
- Review programado: 3 meses después del deploy

Paso a Paso: Cómo Construir Tu Matrix

Paso 1: Define el contexto

Antes de evaluar opciones, documenta tu situación. Sin contexto, los criterios y pesos son arbitrarios.

context = {
    "project": "App RAG para documentación interna",
    "stage": "MVP",
    "team": "1 developer full-time",
    "budget": "$0-50/mes",
    "timeline": "Online en 1 semana",
    "users": "~50 internos",
    "traffic_pattern": "Horario laboral, 9am-6pm",
    "ai_features": ["chat", "search", "document Q&A"],
    "constraints": ["streaming para chat", "no compliance especial"],
}

Paso 2: Selecciona criterios relevantes

No uses los mismos criterios para todos los proyectos. Selecciona los que importan para TU caso:

# Criterios estándar (empezar con estos)
standard_criteria = [
    "Coste mensual",
    "Complejidad operativa",
    "Time-to-deploy",
    "Escalabilidad",
    "Control",
]

# Criterios AI-specific (agregar según caso)
ai_criteria = [
    "Cold starts (latencia primera request)",
    "Streaming support (SSE/WebSocket)",
    "Memory para modelos/embeddings",
    "GPU availability",
    "Timeout limits",
]

# Para este caso: standard + streaming + cold starts
selected_criteria = standard_criteria + [
    "Streaming support",
    "Cold starts",
]

Paso 3: Asigna pesos (deben sumar 100)

Los pesos reflejan TUS prioridades, no "mejores prácticas" universales:

weights = {
    "Coste mensual": 25,         # Budget es constraint principal
    "Complejidad operativa": 20, # Solo yo, necesito que sea simple
    "Time-to-deploy": 20,        # Necesito MVP rápido
    "Escalabilidad": 10,         # No urgente, 50 usuarios
    "Control": 5,                # Sin compliance
    "Streaming support": 15,     # Feature clave para chat
    "Cold starts": 5,            # Aceptable si <3s
}

assert sum(weights.values()) == 100, "Pesos deben sumar 100"

Paso 4: Evalúa cada opción (1-5)

Sé honesto y específico. "5" no significa "perfecto" — significa "la mejor opción en esta dimensión."

evaluation = {
    "Coste mensual": {
        "Local": 4,        # VPS $24/mes, predecible
        "Serverless": 5,   # ~$1-5/mes a este volumen
        "Managed": 5,      # Free tier cubre MVP
        "Self-hosted": 1,  # $30 infra + $400 ops
    },
    "Streaming support": {
        "Local": 5,        # FastAPI + uvicorn nativo
        "Serverless": 1,   # Lambda no soporta streaming
        "Managed": 4,      # Railway soporta WebSockets
        "Self-hosted": 5,  # Control total
    },
    # ... demás criterios
}

Paso 5: Calcula scores ponderados

def calculate_matrix(weights: dict, evaluation: dict) -> dict:
    """Calcula scores ponderados para cada opción."""
    options = ["Local", "Serverless", "Managed", "Self-hosted"]
    scores = {opt: 0 for opt in options}
    
    for criterion, weight in weights.items():
        for option in options:
            score = evaluation[criterion][option]
            scores[option] += weight * score
    
    return dict(sorted(scores.items(), key=lambda x: x[1], reverse=True))

results = calculate_matrix(weights, evaluation)
# {'Managed': 415, 'Serverless': 400, 'Local': 340, 'Self-hosted': 225}

Paso 6: Documenta la decisión y condiciones de re-evaluación

La decisión no es solo "qué elegí" sino "bajo qué condiciones re-evaluaré":

decision = {
    "recommendation": "Managed (Railway)",
    "score": 415,
    "runner_up": "Local (VPS)",
    "runner_up_score": 340,
    "reasoning": "Streaming support descartó Serverless a pesar de buen score. "
                 "Railway ofrece WebSockets + free tier + deploy en minutos.",
    "re_evaluate_when": [
        "Tráfico supere 10K req/día",
        "Necesite GPU para modelos locales",
        "Budget cambie a >$100/mes",
        "Railway cambie pricing o descontinúe features",
    ],
    "review_date": "3 meses desde deploy",
}

Patrones Comunes de Decision Matrix

Patrón 1: MVP con presupuesto cero

Peso dominante: Coste (40) + Time-to-deploy (30)
Resultado típico: Managed (free tier) gana
Excepción: Si necesitas streaming y la plataforma no lo soporta → Local

Patrón 2: Startup en crecimiento

Peso dominante: Escalabilidad (30) + Coste (25)
Resultado típico: Serverless o Managed con auto-scale
Excepción: Si workload es constante (no picos) → Local (VPS, coste fijo)

Patrón 3: Enterprise con compliance

Peso dominante: Control (35) + Seguridad (25, criterio extra)
Resultado típico: Self-hosted o Local en VPC privada
Excepción: Raramente — compliance suele requerir control

Patrón 4: AI con modelos locales

Peso dominante: Memory/GPU (30) + Control (25)
Resultado típico: Self-hosted (GPU instances)
Excepción: Si modelo es pequeño (<4GB) → Local en VPS con RAM suficiente

Ejemplo Completo: Decision Matrix de Principio a Fin

Vamos a construir una decision matrix completa para un caso concreto. Sigue cada paso y al final tendrás un resultado numérico con recomendación.

El caso: NotifyAI

NotifyAI es un servicio que analiza mentions de tu marca en redes sociales y envía un resumen diario por email usando un LLM.

context = {
    "project": "NotifyAI — Social media AI monitor",
    "stage": "MVP",
    "team": "1 developer (tú)",
    "budget": "$0-25/mes",
    "users": "Solo tú + 3 clientes beta",
    "traffic": "~200 requests/día (análisis de mentions)",
    "ai_features": ["Sentiment analysis", "Summary generation"],
    "constraints": ["Sin streaming", "Latencia no crítica (batch process)"],
}

Paso a paso con NotifyAI

# Paso 1: Criterios y pesos (suman 100)
criteria = {
    "Coste mensual":        35,  # Budget es la constraint #1
    "Complejidad operativa": 25,  # Solo yo, necesito algo simple
    "Time-to-deploy":       25,  # Quiero lanzar esta semana
    "Escalabilidad":         5,  # 200 req/día, no necesito escala
    "Control":              10,  # Sin compliance, pero quiero debug fácil
}

# Paso 2: Evaluación (1-5) con justificación
evaluation = {
    "Coste mensual":         {"Local": 4, "Serverless": 5, "Managed": 5, "Self-hosted": 1},
    "Complejidad operativa": {"Local": 3, "Serverless": 4, "Managed": 5, "Self-hosted": 1},
    "Time-to-deploy":        {"Local": 3, "Serverless": 3, "Managed": 5, "Self-hosted": 1},
    "Escalabilidad":         {"Local": 2, "Serverless": 5, "Managed": 3, "Self-hosted": 4},
    "Control":               {"Local": 4, "Serverless": 2, "Managed": 3, "Self-hosted": 5},
}

# Paso 3: Cálculo
options = ["Local", "Serverless", "Managed", "Self-hosted"]
totals = {opt: 0 for opt in options}

print(f"{'Criterio':<25} {'Peso':>4} | {'Local':>6} {'Server':>7} {'Managed':>8} {'Self':>6}")
print("-" * 70)

for criterion, weight in criteria.items():
    scores = evaluation[criterion]
    row = f"{criterion:<25} {weight:>4} |"
    for opt in options:
        weighted = weight * scores[opt]
        totals[opt] += weighted
        row += f" {weighted:>6}"
    print(row)

print("-" * 70)
row = f"{'TOTAL':<25} {'':>4} |"
for opt in options:
    row += f" {totals[opt]:>6}"
print(row)

Output:

Criterio                  Peso | Local  Server  Managed   Self
----------------------------------------------------------------------
Coste mensual               35 |    140    175      175     35
Complejidad operativa       25 |     75    100      125     25
Time-to-deploy              25 |     75     75      125     25
Escalabilidad                5 |     10     25       15     20
Control                     10 |     40     20       30     50
----------------------------------------------------------------------
TOTAL                          |    340    395      470    155

Decisión: Managed (470) gana por margen claro. Para un MVP de 200 req/día con 1 developer y budget mínimo, Railway con free tier es la opción óptima. Serverless (395) es alternativa viable. Self-hosted (155) está fuera de discusión.

Script reutilizable: decision_matrix.py

# decision_matrix.py — Copia, modifica los datos, ejecuta

def run_decision_matrix(criteria: dict, evaluation: dict):
    """Ejecuta una decision matrix completa y muestra resultados."""
    options = list(list(evaluation.values())[0].keys())

    assert sum(criteria.values()) == 100, f"Pesos suman {sum(criteria.values())}, deben sumar 100"

    totals = {opt: 0 for opt in options}
    for criterion, weight in criteria.items():
        for opt in options:
            totals[opt] += weight * evaluation[criterion][opt]

    max_possible = sum(criteria.values()) * 5
    sorted_results = sorted(totals.items(), key=lambda x: x[1], reverse=True)

    print(f"\n{'='*50}")
    print(f"  DECISION MATRIX RESULTS")
    print(f"{'='*50}")
    for rank, (opt, score) in enumerate(sorted_results, 1):
        pct = score / max_possible * 100
        bar = "#" * int(pct / 5)
        marker = " ← RECOMENDADO" if rank == 1 else ""
        print(f"  {rank}. {opt:<15} {score:>4}/{max_possible}  ({pct:.0f}%)  {bar}{marker}")
    print(f"{'='*50}\n")

    winner, w_score = sorted_results[0]
    runner, r_score = sorted_results[1]
    gap = (w_score - r_score) / max_possible * 100
    if gap < 5:
        print(f"  ⚠️  Resultado reñido ({gap:.1f}% diferencia). Considera factores cualitativos.")
    else:
        print(f"  ✅ Resultado claro ({gap:.1f}% diferencia). {winner} es la recomendación.")

    return sorted_results


# === USA TUS DATOS AQUÍ ===
my_criteria = {
    "Coste mensual": 30,
    "Complejidad": 25,
    "Time-to-deploy": 20,
    "Escalabilidad": 15,
    "Control": 10,
}

my_evaluation = {
    "Coste mensual":  {"Local": 4, "Serverless": 5, "Managed": 4, "Self-hosted": 1},
    "Complejidad":    {"Local": 3, "Serverless": 4, "Managed": 5, "Self-hosted": 1},
    "Time-to-deploy": {"Local": 3, "Serverless": 4, "Managed": 5, "Self-hosted": 1},
    "Escalabilidad":  {"Local": 2, "Serverless": 5, "Managed": 3, "Self-hosted": 4},
    "Control":        {"Local": 4, "Serverless": 2, "Managed": 3, "Self-hosted": 5},
}

run_decision_matrix(my_criteria, my_evaluation)

Cuándo Re-evaluar Tu Decision Matrix

Triggers de re-evaluación

Una decision matrix no es un documento estático. Hay señales claras de que necesitas volver a evaluarla:

TriggerEjemploAcción
Cambio de stagePasas de MVP a Growth (>1K usuarios)Re-evalúa escalabilidad y coste
Cambio de presupuestoTu empresa aprueba $500/mes para infraRe-evalúa opciones antes descartadas por coste
Nuevo requerimiento técnicoNecesitas streaming que antes no teníasRe-evalúa: Lambda queda descartado
Cambio en el equipoContratas un DevOpsSelf-hosted se vuelve viable
Cambio de pricing del proveedorRailway sube precios o elimina free tierRe-evalúa alternativas
Incidentes repetidos3+ caídas en un mes con tu estrategia actualEvalúa migración

Frecuencia recomendada

  • Revisión programada: Cada 3 meses
  • Revisión por trigger: Inmediatamente cuando ocurre un trigger
  • Revisión light: Mensualmente, verifica que los costes reales coinciden con las estimaciones

Qué documentar en cada re-evaluación

## Re-evaluación: [Fecha]
- Trigger: [Qué motivó la revisión]
- Datos actuales: [Tráfico real, costes reales, incidentes]
- ¿Cambió el ranking? [Sí/No]
- Acción: [Mantener / Migrar a X / Investigar X]

Errores Comunes en Decision Matrices

Error 1: Pesos uniformes

❌ Todos los criterios pesan 20 (5 × 20 = 100)
   → Todas las dimensiones "importan igual"
   → Resultado: Siempre gana la opción más "balanceada" (managed)
   → Problema: No refleja TUS prioridades

✅ Pesos diferenciados según TU caso
   → Si el coste es tu constraint, dale 30-40
   → Si el control es irrelevante, dale 5
   → El resultado refleja TU realidad

Error 2: Evaluar opciones sin datos

❌ "Serverless: Escalabilidad = 5" (porque "Lambda escala")
   → Sin verificar si escala bien para TU workload

✅ "Serverless: Escalabilidad = 4" 
   → Lambda escala automáticamente, pero cold starts de 8s
     con mis dependencias reducen la utilidad del auto-scale
     para requests interactivos

Error 3: No documentar condiciones de re-evaluación

❌ Decisión: "Usamos Railway"
   → ¿Cuándo dejamos de usarlo? ¿Si crecemos? ¿Si cambian precios?

✅ Decisión: "Usamos Railway"
   Re-evaluar cuando:
   - Tráfico > 500 req/hora (verificar limits)
   - Budget > $100/mes (considerar VPS)
   - Necesitemos servicios AWS (S3, SageMaker)
   Review: Q2 2026

Troubleshooting

Problema 1: "Mis pesos se sienten arbitrarios"

Solución: Usa la técnica de comparación por pares. Para cada par de criterios, pregúntate: "¿Prefiero optimizar A o B?" El criterio que gana más comparaciones recibe más peso. Para 5 criterios, hay 10 comparaciones — en 5 minutos tienes pesos más robustos que "a ojo."

Problema 2: "El ganador tiene un deal-breaker funcional"

Solución: Los deal-breakers no se capturan en scores numéricos. Antes de calcular, identifica deal-breakers: si necesitas streaming, Lambda tiene score 0 (no 1-5) en esa dimensión. Elimina opciones con deal-breakers antes de la evaluación numérica.

Problema 3: "Mi matrix da un empate técnico"

Solución: Un empate (<5% diferencia) significa que ambas opciones son viables para tu caso. Elige por: (1) familiaridad del equipo, (2) facilidad de migración futura, o (3) la que tenga mejor documentación. Documenta que fue empate y por qué elegiste la que elegiste.

Problema 4: "No tengo datos reales para evaluar"

Solución: Usa estimaciones conservadoras (peor caso razonable) y marca las evaluaciones como "estimadas." Después de 2-4 semanas en producción, actualiza con datos reales. La primera matrix es una hipótesis informada — no necesita ser perfecta para ser útil.


Ejercicios Prácticos

Ejercicio 1: Construye tu matrix desde cero

Usa el template de esta cápsula para construir una decision matrix para tu app AI. Incluye contexto, criterios con pesos, evaluación 1-5, y score ponderado.

Ver solución

No hay solución fija — depende de tu caso. Verifica que:

  • Los pesos suman 100
  • Cada evaluación tiene justificación (no solo números)
  • Hay condiciones de re-evaluación documentadas
  • La decisión incluye segunda opción y descartados con razón

Si tu matrix produce un ganador claro (>20% sobre la segunda opción), tu caso tiene constraints fuertes. Si está reñido (<10% diferencia), cualquiera de los top-2 es viable — elige por experiencia o preferencia.

Ejercicio 2: Sensitivity analysis

Toma tu matrix del Ejercicio 1. Cambia el peso del criterio #1 de su valor actual a 10 menos. ¿Cambia el ganador?

Ver solución

Si al cambiar un peso ±10 puntos el ganador cambia, tu decisión es sensible a ese criterio. Eso significa:

  • Necesitas ser muy preciso en la evaluación de ese criterio
  • Un cambio en tus circunstancias (ej: más presupuesto) puede cambiar la recomendación
  • Documenta esto en las condiciones de re-evaluación

Si el ganador no cambia, tu decisión es robusta — la opción gana bajo múltiples combinaciones de pesos.

Ejercicio 3: Matrix para un caso diferente al tuyo

Construye una matrix para un caso opuesto al tuyo: si tu caso es MVP con 1 developer, haz una matrix para enterprise con equipo de 10. ¿Cambian los criterios? ¿Los pesos?

Ver solución

En un caso enterprise con equipo de 10:

  • Control pesa más (compliance, auditoría)
  • Coste pesa menos (presupuesto mayor)
  • Escalabilidad pesa más (más usuarios)
  • Time-to-deploy pesa menos (proceso más formal)
  • Criterios nuevos: SLA, multi-region, disaster recovery

El resultado probablemente favorece Self-hosted o AWS managed services en lugar de Railway/Render. Este ejercicio demuestra que la matrix es un framework, no una respuesta fija.

Ejercicio 4: Pitch la decisión

Prepara una presentación de 2 minutos de tu decision matrix para un stakeholder no-técnico. Incluye: el contexto, los 3 criterios más importantes, el ganador, y cuándo re-evaluar.

Ver solución

Ejemplo:

"Necesitamos deployar nuestra app AI de documentación. Evalué 4 opciones de infraestructura usando 7 criterios ponderados por nuestras prioridades: presupuesto limitado, equipo de una persona, y necesidad de streaming para el chat.

Railway ganó con 415 puntos de 500 posibles. Las dos razones principales: (1) free tier cubre nuestro volumen actual, y (2) soporta WebSockets que necesitamos para streaming. La alternativa es un servidor propio con Docker, que nos da más control pero cuesta más en mi tiempo.

Re-evaluamos en 3 meses o si superamos 500 requests/hora. Tengo la matrix documentada para cuando lo necesitemos."

Clave: Simplifica para no-técnicos. "4 opciones, 7 criterios, Railway gana por coste y features."


Resumen

  • Una decision matrix convierte análisis de trade-offs en una decisión documentada y defendible.
  • Los 6 pasos son: contexto → criterios → pesos → evaluación → scores → decisión + re-evaluación.
  • Los pesos deben reflejar TUS prioridades, no best practices genéricas. Que sumen 100.
  • La evaluación (1-5) debe tener justificación, no solo números.
  • Los errores más comunes son: pesos uniformes, evaluación sin datos, y no documentar condiciones de re-evaluación.
  • Una buena matrix incluye sensitivity analysis: ¿cambia el resultado si cambio un peso?
  • La matrix se reutiliza: Módulo 7 (plataformas), Módulo 8 (proyecto integrador).

Recursos Adicionales

  1. Decision Matrix Analysis — MindTools — Framework general de decision matrices
  2. Pugh Matrix — Método formal de decisión ponderada
  3. Architecture Decision Records (ADR) — Formato para documentar decisiones de arquitectura
  4. DACI Framework — Framework para decisiones en equipo
  5. AWS Well-Architected Tool — Herramienta de evaluación de arquitectura
  6. Technology Radar — ThoughtWorks — Referencia para evaluar tecnologías