Módulo 8: Proyecto Integrador — Deployed AI System

6. Decision Matrix Final — Documenta Tu Decisión

Descripción

En esta cápsula vas a integrar las decision matrices del Módulo 1 (estrategias) y Módulo 7 (plataformas) en una decisión final aplicada: la decision matrix v_final. No es un ejercicio teórico — es la documentación de POR QUÉ tu sistema AI está desplegado donde está, con qué estrategia, y qué trade-offs aceptaste. Este documento es la justificación que presentas en una entrevista, que le envías a tu tech lead, o que consultas cuando necesitas re-evaluar.

Contexto: En el Módulo 1, construiste un framework de decisión. En el Módulo 7, lo extendiste con criterios de plataforma. Ahora lo aplicas. La diferencia entre v2 (M7) y v_final (M8) es que v_final incluye la experiencia real del deployment: lo que funcionó, lo que no, y lo que cambiarías.


La Evolución de la Decision Matrix

De framework a decisión aplicada

Módulo 1 — v1: Framework teórico
├── 4 estrategias evaluadas (Local, Serverless, Managed, Self-hosted)
├── 5 dimensiones base (Coste, Complejidad, Escalabilidad, Control, Time-to-deploy)
├── Decision matrix con pesos hipotéticos
└── Resultado: "Para mi caso, la estrategia recomendada es [X]"

Módulo 7 — v2: Extendida con plataformas
├── Estrategia elegida + plataforma específica
├── Criterios de plataforma (free tier, DX, deploy flow)
├── Comparativa Render vs Railway vs Fly.io vs AWS
└── Resultado: "Dentro de [estrategia X], elijo [plataforma Y]"

Módulo 8 — v_final: Aplicada y validada
├── Decisión ejecutada: estrategia + plataforma con evidencia
├── Trade-offs REALES (no teóricos) del deployment
├── Qué funcionó, qué no, qué cambiaría
├── Condiciones de re-evaluación con métricas reales
└── Resultado: "Mi sistema está en [plataforma Y] porque [justificación con datos]"

La v_final es el documento más valioso porque tiene datos reales, no estimaciones.


Estructura de la Decision Matrix v_final

Template completo

# Decision Matrix v_final — [Nombre del Sistema AI]

**Fecha:** [Fecha]
**Autor:** [Tu nombre]
**Sistema:** [Nombre de tu app AI]
**URL producción:** [URL real]

---

## 1. Decisión Final

### Estrategia elegida: [Managed/Serverless/Local/Self-hosted]
### Plataforma elegida: [Railway/Render/Fly.io/AWS]

**En una frase:** [Por qué esta combinación es la correcta para tu caso]

---

## 2. Contexto del Proyecto (Actualizado)

| Dato | Valor al decidir (M1) | Valor actual (M8) |
|------|----------------------|-------------------|
| Stage | [MVP/Growth/Scale] | [Actual] |
| Usuarios | [Estimación] | [Real o estimado actual] |
| Requests/día | [Estimación] | [Real o estimado actual] |
| Presupuesto | [Rango] | [Gasto real] |
| Equipo | [Tamaño] | [Tamaño actual] |
| Latencia target | [Valor] | [Valor medido] |

---

## 3. Decision Matrix Ponderada

### Criterios y Pesos

| # | Criterio | Peso | Justificación del peso |
|---|----------|------|----------------------|
| 1 | [Criterio] | [N] | [Por qué este peso] |
| 2 | [Criterio] | [N] | [Por qué este peso] |
| ... | ... | ... | ... |
| | **Total** | **100** | |

### Evaluación (1-5)

| Criterio (Peso) | Local | Serverless | Managed | Self-hosted |
|-----------------|:-----:|:----------:|:-------:|:-----------:|
| [C1] ([P1]) | [score] | [score] | [score] | [score] |
| ... | ... | ... | ... | ... |
| **TOTAL PONDERADO** | **[X]** | **[X]** | **[X]** | **[X]** |

### Evaluación de Plataforma (dentro de estrategia elegida)

| Criterio | Render | Railway | Fly.io | AWS |
|----------|:------:|:-------:|:------:|:---:|
| Free tier | [1-5] | [1-5] | [1-5] | [1-5] |
| Deploy simplicity | [1-5] | [1-5] | [1-5] | [1-5] |
| DX (developer experience) | [1-5] | [1-5] | [1-5] | [1-5] |
| Scaling options | [1-5] | [1-5] | [1-5] | [1-5] |
| Pricing transparency | [1-5] | [1-5] | [1-5] | [1-5] |
| Docker support | [1-5] | [1-5] | [1-5] | [1-5] |
| **TOTAL** | **[X]** | **[X]** | **[X]** | **[X]** |

---

## 4. Justificación Detallada

### Por qué [estrategia] y no las otras

[3-5 párrafos explicando la decisión con datos concretos]

### Por qué [plataforma] y no las otras

[2-3 párrafos con razones específicas]

### Trade-offs aceptados

| Trade-off | Qué ganamos | Qué sacrificamos |
|-----------|-------------|-------------------|
| [Trade-off 1] | [Beneficio] | [Coste] |
| [Trade-off 2] | [Beneficio] | [Coste] |
| [Trade-off 3] | [Beneficio] | [Coste] |

---

## 5. Validación Post-Deployment

### Lo que funcionó como esperábamos
- ✅ [Aspecto 1]
- ✅ [Aspecto 2]

### Lo que fue diferente a lo esperado
- ⚠️ [Aspecto 1 — qué esperábamos vs qué pasó]
- ⚠️ [Aspecto 2]

### Lo que cambiaríamos
- [Cambio 1 — por qué]
- [Cambio 2 — por qué]

---

## 6. Condiciones de Re-evaluación

| Trigger | Métrica | Acción |
|---------|---------|--------|
| [Trigger 1] | [Métrica específica] | [Qué hacer] |
| [Trigger 2] | [Métrica específica] | [Qué hacer] |
| [Trigger 3] | [Métrica específica] | [Qué hacer] |

**Review programado:** [Fecha, quién, qué evaluar]

Ejemplo Completo: DocuSearch AI

Decision Matrix v_final aplicada

# Datos del proyecto aplicados
project_context = {
    "name": "DocuSearch AI",
    "type": "RAG",
    "strategy_chosen": "Managed",
    "platform_chosen": "Railway",
    "url": "https://docusearch-ai.railway.app",
    "deploy_date": "2026-03-08",
}

# Criterios con pesos (del M1, validados en M8)
criteria_final = {
    "Coste mensual total": {
        "weight": 20,
        "justification": "Presupuesto <$50/mes, es constraint dura",
    },
    "Complejidad operativa": {
        "weight": 25,
        "justification": "Equipo de 2, no podemos dedicar hrs/semana a ops",
    },
    "Time-to-deploy": {
        "weight": 20,
        "justification": "Iteramos prompts diariamente, deploy rápido = mejor sistema",
    },
    "Escalabilidad": {
        "weight": 10,
        "justification": "150 usuarios internos, no esperamos 10x en 3 meses",
    },
    "Control": {
        "weight": 10,
        "justification": "Datos internos pero no regulados, control medio suficiente",
    },
    "Memory (ChromaDB)": {
        "weight": 15,
        "justification": "ChromaDB necesita ~2GB RAM persistente",
    },
}

# Evaluación final con datos reales
evaluation_final = {
    "Coste mensual total": {
        "Local (VPS)":     (4, "$24/mes VPS + $15 APIs = $39"),
        "Serverless":      (2, "ChromaDB stateless no funciona en Lambda"),
        "Managed (Rwy)":   (4, "$5/mes Railway + $15 APIs = $20"),
        "Self-hosted":     (1, "EC2 $30 + tiempo ops = $200+"),
    },
    "Complejidad operativa": {
        "Local (VPS)":     (2, "Requiere mantener VPS, updates, SSL"),
        "Serverless":      (3, "AWS gestiona infra pero debugging Lambda es complejo"),
        "Managed (Rwy)":   (5, "Git push y listo, Railway gestiona todo"),
        "Self-hosted":     (1, "Todo manual, requiere expertise DevOps"),
    },
    "Time-to-deploy": {
        "Local (VPS)":     (3, "5-15 min con CI/CD"),
        "Serverless":      (4, "2-5 min con SAM"),
        "Managed (Rwy)":   (5, "2 min desde git push"),
        "Self-hosted":     (1, "20+ min con todo el proceso"),
    },
    "Escalabilidad": {
        "Local (VPS)":     (2, "Vertical limitada"),
        "Serverless":      (5, "Auto-scaling nativo"),
        "Managed (Rwy)":   (3, "Scaling manual pero simple"),
        "Self-hosted":     (4, "Configurable pero complejo"),
    },
    "Control": {
        "Local (VPS)":     (4, "SSH directo, control de OS"),
        "Serverless":      (2, "Solo código, sin acceso a infra"),
        "Managed (Rwy)":   (3, "Variables, logs, métricas básicas"),
        "Self-hosted":     (5, "Control total"),
    },
    "Memory (ChromaDB)": {
        "Local (VPS)":     (5, "4GB+ de RAM disponible"),
        "Serverless":      (1, "Lambda es stateless, ChromaDB no persiste"),
        "Managed (Rwy)":   (4, "Railway Pro permite hasta 8GB"),
        "Self-hosted":     (5, "Ilimitado"),
    },
}

Cálculo de scores

def calculate_final_scores(criteria: dict, evaluation: dict) -> dict:
    """Calcula scores ponderados para la decision matrix v_final."""
    strategies = ["Local (VPS)", "Serverless", "Managed (Rwy)", "Self-hosted"]
    scores = {s: 0 for s in strategies}
    breakdown = {s: [] for s in strategies}

    for criterion, config in criteria.items():
        weight = config["weight"]
        for strategy in strategies:
            raw_score, note = evaluation[criterion][strategy]
            weighted = weight * raw_score
            scores[strategy] += weighted
            breakdown[strategy].append({
                "criterion": criterion,
                "weight": weight,
                "raw": raw_score,
                "weighted": weighted,
                "note": note,
            })

    max_possible = sum(c["weight"] for c in criteria.values()) * 5  # max score = 5

    return {
        "scores": dict(sorted(scores.items(), key=lambda x: x[1], reverse=True)),
        "max_possible": max_possible,
        "breakdown": breakdown,
    }

results = calculate_final_scores(criteria_final, evaluation_final)

print("=== Decision Matrix v_final ===")
print(f"Max possible: {results['max_possible']}")
print()
for strategy, score in results["scores"].items():
    pct = (score / results["max_possible"]) * 100
    bar = "█" * int(pct / 5) + "░" * (20 - int(pct / 5))
    print(f"  {strategy:20s} {score:4d}/{results['max_possible']} ({pct:.0f}%) {bar}")

Output esperado

=== Decision Matrix v_final ===
Max possible: 500

  Managed (Rwy)         410/500 (82%) ████████████████░░░░
  Local (VPS)            310/500 (62%) ████████████░░░░░░░░
  Serverless             265/500 (53%) ██████████░░░░░░░░░░
  Self-hosted            230/500 (46%) █████████░░░░░░░░░░░

Sección de Validación Post-Deployment

Después de desplegar, actualiza tu matrix

post_deploy_validation = {
    "what_worked": [
        "Railway deploy desde git push: 2 min consistently",
        "Free tier cubrió los primeros 2 meses sin coste de infra",
        "Developer experience excelente: logs, métricas, easy rollback",
        "ChromaDB funciona bien con 2GB de RAM en Railway",
    ],
    "what_was_different": [
        {
            "expected": "Latencia de inferencia <3s",
            "actual": "First request después de inactividad: 5-15s (cold start en free tier)",
            "impact": "UX degradada para el primer usuario de cada sesión",
        },
        {
            "expected": "Railway Pro necesario desde día 1",
            "actual": "Free tier fue suficiente para los primeros 150 usuarios",
            "impact": "Ahorro de $5/mes por 2 meses",
        },
    ],
    "what_would_change": [
        "Habría empezado con Railway Pro para eliminar cold starts desde día 1",
        "Habría implementado caching antes — 30% de queries son repetitivas",
    ],
}

Condiciones de re-evaluación con datos reales

re_evaluation_triggers = {
    "scale_trigger": {
        "metric": "Usuarios activos diarios",
        "threshold": "> 500",
        "action": "Evaluar Railway Pro vs VPS con más RAM",
        "current_value": 150,
    },
    "cost_trigger": {
        "metric": "Factura mensual total (infra + APIs)",
        "threshold": "> $100/mes",
        "action": "Evaluar VPS (coste fijo) vs managed (variable)",
        "current_value": "$20/mes",
    },
    "latency_trigger": {
        "metric": "Latencia p95 de inferencia",
        "threshold": "> 5s excluyendo cold starts",
        "action": "Evaluar infra con más recursos o caching",
        "current_value": "2.8s p95",
    },
    "compliance_trigger": {
        "metric": "Requerimiento de SOC2 o datos sensibles",
        "threshold": "Cualquier requerimiento nuevo",
        "action": "Evaluar self-hosted o AWS con VPC",
        "current_value": "No requerido",
    },
}

review_schedule = {
    "next_review": "3 meses desde deploy",
    "reviewer": "[Tu nombre]",
    "metrics_to_check": [
        "Tráfico real vs estimado",
        "Costes reales vs estimados",
        "Incidentes de downtime (count, duration)",
        "Feedback de usuarios sobre latencia",
        "¿Algún trigger de re-evaluación se activó?",
    ],
}

Cómo Presentar Tu Decisión

El pitch de 2 minutos

Estructura:
1. Contexto (15s): "Tenemos una app RAG para 150 usuarios internos..."
2. Decisión (15s): "Elegimos Railway (managed) porque..."
3. Justificación (45s): "Los 3 criterios que más pesaron fueron..."
4. Trade-offs (30s): "Sacrificamos X a cambio de Y..."
5. Re-evaluación (15s): "Re-evaluamos si..."

Para una entrevista de trabajo

Pregunta: "Cuéntame sobre una decisión de infraestructura que tomaste"

"Para DocuSearch AI, un sistema RAG que sirve documentación interna,
evalué 4 estrategias de deployment en 6 dimensiones ponderadas.

Elegí Railway (managed platform) sobre AWS (self-hosted) porque:
- Con un equipo de 2, no podíamos dedicar 5+ hrs/semana a ops
- El presupuesto de <$50/mes descartó self-hosted
- ChromaDB requiere memoria persistente, lo que descartó serverless
- El time-to-deploy de 2 minutos nos permite iterar prompts rápidamente

El trade-off fue menos control: no tenemos SSH al servidor ni
configuración granular de networking. Pero para nuestro caso —
datos internos no regulados, 150 usuarios — era un trade-off aceptable.

Documenté la decisión con una decision matrix que incluye criterios
ponderados, estimación de costes, y triggers de re-evaluación.
Re-evaluamos si alcanzamos 500 usuarios o si la latencia p95 supera 5 segundos."

Troubleshooting

Problema 1: "Mi decision matrix del M1 ya no refleja mi caso actual"

Causa: Los datos cambiaron entre M1 (cuando planificaste) y M8 (cuando ejecutaste).

Solución: Eso es exactamente lo que documenta la v_final. La sección "Validación Post-Deployment" captura las diferencias. No modifiques la v1 o v2 — mantenlas como evidencia de evolución. La v_final documenta la realidad.

Problema 2: "Dos estrategias siguen muy cercanas en score"

Solución:

# Sensitivity analysis: cambia un peso ±10 y observa
sensitivity_test = {
    "Original weights": {"Managed": 410, "Local": 310},
    "Complejidad +10, Escalabilidad -10": {"Managed": 430, "Local": 290},
    "Coste +10, Time-to-deploy -10": {"Managed": 400, "Local": 320},
}
# Si Managed gana en todos los escenarios → decisión robusta
# Si depende del escenario → documenta ambas como viables

Problema 3: "Mi manager quiere ver la matrix pero no entiende pesos y scores"

Solución: Extrae un resumen ejecutivo de 3 líneas al inicio del documento:

## Resumen Ejecutivo
Elegimos Railway (managed) para DocuSearch AI.
Coste: $20/mes. Deploy: 2 minutos desde git push. Uptime: 99.5%.
Re-evaluamos en 3 meses o si llegamos a 500 usuarios diarios.

Problema 4: "Elegí una plataforma diferente a lo que mi matrix recomendó"

Solución: Documéntalo honestamente. A veces elegimos por factores no cuantificables (familiaridad con la herramienta, recomendación de un colega, tutorial que vimos). Documenta: "La matrix recomendaba X, elegí Y porque [razón]. El trade-off es [qué sacrifiqué]."


Ejercicios Prácticos

Ejercicio 1: Decision matrix v_final con tus datos reales

Toma tu decision matrix del M1/M7 y actualízala con datos reales del deployment.

Ver solución
# Usa el template de criteria y evaluation mostrado arriba.
# Pasos:
# 1. Copia tus criterios y pesos del M1
# 2. Actualiza las evaluaciones (1-5) con experiencia real
# 3. Ejecuta calculate_final_scores()
# 4. Compara con lo que recomendó tu v1

# Ejemplo de actualización:
# M1 evaluación: "Managed time-to-deploy: 5 (estimado 1-3 min)"
# M8 evaluación: "Managed time-to-deploy: 5 (medido: 2 min consistentemente)"
# → El score no cambia pero ahora tiene datos reales

# M1 evaluación: "Serverless coste: 5 (estimado $5/mes)"
# M8 evaluación: "Serverless coste: 1 (ChromaDB no funciona en Lambda)"
# → El score cambia drásticamente con experiencia real

La v_final tiene autoridad sobre v1 y v2 porque está basada en evidencia, no estimaciones.

Ejercicio 2: Sección de validación post-deployment

Documenta qué funcionó, qué fue diferente, y qué cambiarías.

Ver solución
## Validación Post-Deployment

### Lo que funcionó como esperábamos
- ✅ Deploy desde git push en <3 minutos consistentemente
- ✅ Costes dentro del presupuesto ($20/mes vs $50 budget)
- ✅ Health checks funcionan y detectan problemas rápidamente
- ✅ Rollback disponible con un click desde el dashboard

### Lo que fue diferente a lo esperado
- ⚠️ Cold starts en free tier: esperábamos 0ms, medimos 5-15s
  Impacto: Primer request de cada sesión es lento
- ⚠️ Logs más limitados de lo que pensábamos
  Impacto: Debugging requiere agregar más logging al código
- ⚠️ Latencia a OpenAI varía entre 1-4s (pensábamos 1-2s consistente)
  Impacto: Latencia total más variable de lo estimado

### Lo que cambiaríamos
- Habríamos empezado con Railway Pro ($5/mes) para evitar cold starts
- Habríamos implementado caching de responses desde el día 1
- Habríamos configurado structured logging antes del deploy

Ejercicio 3: Prepara el pitch de 2 minutos

Escribe tu pitch siguiendo la estructura: contexto → decisión → justificación → trade-offs → re-evaluación.

Ver solución
Mi pitch:

CONTEXTO: "Construí [nombre], una app [tipo] que [qué hace],
para [cuántos usuarios], con un presupuesto de [presupuesto]."

DECISIÓN: "Elegí [plataforma] como estrategia [tipo] de deployment."

JUSTIFICACIÓN: "Los criterios que más pesaron fueron:
1. [Criterio 1 con peso X%] — porque [razón]
2. [Criterio 2 con peso Y%] — porque [razón]
3. [Criterio 3 con peso Z%] — porque [razón]"

TRADE-OFFS: "Sacrifiqué [qué] a cambio de [qué].
Concretamente, [ejemplo específico del trade-off]."

RE-EVALUACIÓN: "Re-evalúo la decisión si [trigger 1] o [trigger 2].
La próxima revisión es en [fecha]."

Total: ~2 minutos hablado, cubre todos los puntos clave.

Practica en voz alta. El pitch no es para memorizarlo — es para internalizar la estructura de cómo comunicas decisiones técnicas.

Ejercicio 4: Sensitivity analysis

Cambia los pesos de tu matrix ±10 en los criterios top 2 y verifica si la recomendación cambia.

Ver solución
import copy

def sensitivity_analysis(criteria, evaluation, top_criteria, delta=10):
    """Test de sensibilidad: varía pesos y observa si cambia el ganador."""
    base_results = calculate_final_scores(criteria, evaluation)
    base_winner = list(base_results["scores"].keys())[0]
    print(f"Base winner: {base_winner} ({list(base_results['scores'].values())[0]})")

    for criterion in top_criteria:
        for direction in [+delta, -delta]:
            test_criteria = copy.deepcopy(criteria)
            test_criteria[criterion]["weight"] += direction

            others = [c for c in test_criteria if c != criterion]
            adjust = -direction / len(others)
            for other in others:
                test_criteria[other]["weight"] += adjust

            results = calculate_final_scores(test_criteria, evaluation)
            winner = list(results["scores"].keys())[0]
            change = "CHANGED" if winner != base_winner else "same"
            sign = "+" if direction > 0 else ""
            print(f"  {criterion} {sign}{direction}: winner = {winner} [{change}]")

# Ejecutar:
sensitivity_analysis(
    criteria_final, evaluation_final,
    top_criteria=["Complejidad operativa", "Coste mensual total"]
)

Si el ganador no cambia en ningún escenario, tu decisión es robusta. Si cambia, documenta en qué condiciones cambiaría y por qué aceptas el riesgo.


Resumen

  • La decision matrix v_final integra M1 (estrategias) + M7 (plataformas) + experiencia real del deployment
  • Es la versión más valiosa porque tiene datos reales, no estimaciones
  • Incluye: contexto actualizado, matrix ponderada, justificación, trade-offs, y validación post-deployment
  • La sección "qué fue diferente" es la más honesta y útil del documento
  • Condiciones de re-evaluación con métricas específicas evitan decisiones estancadas
  • El pitch de 2 minutos es cómo comunicas la decisión en entrevistas y reuniones
  • Sensitivity analysis verifica que tu decisión es robusta ante cambios en prioridades

Recursos Adicionales

  1. Architecture Decision Records (ADR) — Formato estándar para documentar decisiones
  2. Lightweight Architecture Decision Records — Versión simplificada de ADRs
  3. Decision Matrix — Wikipedia — Teoría de decision matrices
  4. AWS Well-Architected Framework — Framework de evaluación de arquitectura
  5. Sensitivity Analysis — Wikipedia — Teoría de análisis de sensibilidad
  6. Technology Radar — Thoughtworks — Referencia para evaluar herramientas y plataformas