Módulo 7: Alternative Platforms (Render, Railway, Fly.io)

7. Decision Matrix de Plataformas: El Framework Completo

Descripción

En esta cápsula vas a extender la decision matrix del Módulo 1 con criterios de plataforma. En M1 evaluaste categorías (Local, Serverless, Managed, Self-hosted). Aquí evalúas plataformas concretas dentro de la categoría "Managed": AWS vs Render vs Railway vs Fly.io. El resultado es la decision matrix v2 — un framework completo que mapea requirements de tu proyecto a la plataforma óptima, con datos reales de las cápsulas anteriores.

Contexto: Has desplegado en Render, Railway y Fly.io. Has comparado features, pricing, limitaciones (cápsula 05). Has integrado CI/CD (cápsula 06). Ahora tienes experiencia de primera mano con cada plataforma. La decision matrix v2 formaliza esa experiencia en un framework reutilizable. Cada vez que empieces un proyecto nuevo, esta matrix te dice dónde deployar.


De Categorías a Plataformas

Recap: Decision Matrix v1 (Módulo 1)

En M1 tu matrix evaluaba categorías:

Matrix v1 (M1):
Criterios × [Local, Serverless, Managed, Self-hosted]
→ Resultado: "Para mi caso, Managed es la categoría óptima"

Eso te dice la dirección, pero no la plataforma. "Managed" incluye Render, Railway, Fly.io, Heroku, DigitalOcean App Platform, Google Cloud Run, Azure Container Apps... Necesitas un segundo nivel de decisión.

Decision Matrix v2: Plataformas

Matrix v2 (M7):
Criterios × [AWS, Render, Railway, Fly.io]
→ Resultado: "Para mi caso, Railway es la plataforma óptima"

Framework completo:
Matrix v1 → Categoría → Matrix v2 → Plataforma → Deploy

La v2 no reemplaza la v1 — la extiende. Si tu v1 dice "Serverless," tu plataforma es AWS Lambda (la única opción en esta guía). Si dice "Managed," la v2 elige entre Render, Railway y Fly.io. Si dice "Local," no necesitas plataforma cloud. La v2 solo aplica cuando la categoría óptima es Managed o cuando quieres comparar Managed vs AWS.


Criterios de Plataforma para AI

Criterios estándar (de M1, adaptados)

#CriterioDescripciónRelevancia AI
1Coste mensualInfra + APIs + opsAlta — APIs LLM dominan, pero infra importa
2Developer experienceSetup, CLI, workflows, logsAlta — iteración rápida = mejor producto
3Time-to-deployDe commit a producciónMedia — importa más en MVP que en scale
4EscalabilidadCapacidad de crecer sin migrarMedia — depende del stage
5ControlAcceso a configuración, debuggingBaja para managed, alta para enterprise

Criterios AI-specific (nuevos en v2)

#CriterioDescripciónPor qué es específico de AI
6Request timeoutTiempo máximo para una request HTTPInferencia AI puede tardar 5-30s
7RAM disponibleMemoria máxima del servicioEmbeddings, vectores, modelos en memoria
8WebSocket/streamingSoporte para streaming de tokensChat UX requiere streaming
9Cold startTiempo de arranque después de inactividadAfecta UX en apps con tráfico irregular
10Multi-regionDeploy en múltiples ubicacionesLatencia global para apps con usuarios distribuidos

Criterios de equipo/negocio

#CriterioDescripciónCuándo importa
11Vendor lock-inDificultad de migrar a otra plataformaSiempre — pero más en scale
12Preview environmentsDeploys temporales por PREquipos de 3+ developers
13Databases integradasPostgreSQL, Redis como add-onsCuando tu app necesita persistence
14CI/CD integrationFacilidad de conectar con GitHub ActionsEquipos con CI/CD existente
15Compliance/SLAGarantías de uptime, certificacionesEnterprise, datos regulados

No necesitas todos los criterios. Selecciona 7-10 que sean relevantes para tu caso.


Evaluación de Plataformas: Datos Reales

Evaluación cuantitativa (1-5)

# Evaluación basada en datos reales de las cápsulas 02-06

platform_scores = {
    "AWS (Lambda + S3)": {
        "Coste mensual":      (3, "Pay-per-use, pero setup + monitoring tiene coste oculto"),
        "Developer experience": (2, "IAM, API Gateway, CloudWatch — curva de aprendizaje alta"),
        "Time-to-deploy":     (1, "30-60 min primera vez, 5-10 min redeploy"),
        "Escalabilidad":      (5, "Auto-scaling nativo, prácticamente ilimitado"),
        "Control":            (5, "Configuración granular de todo"),
        "Request timeout":    (5, "Lambda: 15 min máximo"),
        "RAM disponible":     (4, "Lambda: hasta 10 GB. EC2: ilimitado"),
        "WebSocket/streaming": (3, "API Gateway WebSocket existe pero es complejo"),
        "Cold start":         (2, "Lambda cold start: 1-10s, peor con container images"),
        "Multi-region":       (5, "Nativo con configuración"),
        "Vendor lock-in":     (1, "Alto — IAM, API Gateway, CloudWatch, Lambda specifics"),
        "Preview environments": (2, "Manual — stages en API Gateway o stacks separados"),
        "Databases integradas": (4, "RDS, ElastiCache, DynamoDB — todo managed"),
        "CI/CD integration":  (4, "CodePipeline nativo, GitHub Actions con AWS CLI"),
        "Compliance/SLA":     (5, "SOC2, HIPAA, ISO, SLA 99.99%"),
    },
    "Render": {
        "Coste mensual":      (4, "Predecible: $7-85/mes por servicio"),
        "Developer experience": (3, "Dashboard simple, pero sin CLI"),
        "Time-to-deploy":     (4, "3-5 min primera vez, auto-deploy en push"),
        "Escalabilidad":      (3, "Horizontal scaling, pero single region"),
        "Control":            (2, "Limitado — planes fijos, pocas opciones de tuning"),
        "Request timeout":    (2, "30 segundos — limitante para AI"),
        "RAM disponible":     (3, "512 MB - 8 GB según plan"),
        "WebSocket/streaming": (3, "SSE sí, WebSocket solo en Starter+"),
        "Cold start":         (2, "Free: 15-45s. Starter+: 0"),
        "Multi-region":       (1, "4 regiones, pero single region por servicio"),
        "Vendor lock-in":     (4, "Bajo — Docker container + render.yaml simple"),
        "Preview environments": (2, "En beta, no maduro"),
        "Databases integradas": (3, "PostgreSQL y Redis managed"),
        "CI/CD integration":  (3, "Auto-deploy nativo, API para GitHub Actions"),
        "Compliance/SLA":     (2, "Básico — no SOC2, SLA limitado"),
    },
    "Railway": {
        "Coste mensual":      (3, "Pay-per-use: ~$25-30/mes para app AI típica"),
        "Developer experience": (5, "CLI excelente, preview envs, add-ons con un click"),
        "Time-to-deploy":     (5, "2-3 min con CLI, el más rápido"),
        "Escalabilidad":      (3, "Auto-scaling en Pro, pero single region"),
        "Control":            (3, "Más que Render, menos que AWS"),
        "Request timeout":    (4, "5 minutos — suficiente para mayoría de AI"),
        "RAM disponible":     (4, "Hasta 32 GB en Pro"),
        "WebSocket/streaming": (4, "Full WebSocket support"),
        "Cold start":         (3, "2-8s con sleep habilitado"),
        "Multi-region":       (1, "Single region"),
        "Vendor lock-in":     (4, "Bajo — Docker + railway.toml mínimo"),
        "Preview environments": (5, "Nativo, automático, con DB aislada por PR"),
        "Databases integradas": (5, "PostgreSQL, Redis, MySQL, MongoDB con un click"),
        "CI/CD integration":  (4, "Auto-deploy + CLI en GitHub Actions"),
        "Compliance/SLA":     (2, "Básico — Pro tiene SLA pero limitado"),
    },
    "Fly.io": {
        "Coste mensual":      (5, "El más barato: ~$10-15/mes para app AI"),
        "Developer experience": (3, "CLI potente pero más setup inicial"),
        "Time-to-deploy":     (3, "5-7 min primera vez, 2-3 min redeploy"),
        "Escalabilidad":      (5, "Multi-region nativo, auto-start/stop"),
        "Control":            (4, "SSH a VMs, Machines API, configuración granular"),
        "Request timeout":    (4, "Configurable — sin límite hard"),
        "RAM disponible":     (3, "256 MB - 16 GB+"),
        "WebSocket/streaming": (5, "Full support, nativo"),
        "Cold start":         (4, "Firecracker: 300ms-2s"),
        "Multi-region":       (5, "30+ regiones, anycast routing"),
        "Vendor lock-in":     (3, "Medio — fly.toml, volumes, multi-region config"),
        "Preview environments": (2, "Manual con Machines API"),
        "Databases integradas": (3, "Fly Postgres + Upstash Redis"),
        "CI/CD integration":  (4, "Action oficial + flyctl en CI"),
        "Compliance/SLA":     (3, "Mejor que Render/Railway, pero no enterprise"),
    },
}

Construir Tu Decision Matrix v2

Paso 1: Seleccionar criterios relevantes

No todos los criterios aplican a tu caso. Selecciona 7-10:

def select_criteria(project_profile: dict) -> dict:
    """Selecciona criterios y pesos basados en el perfil del proyecto."""

    base_criteria = {
        "Coste mensual": 15,
        "Developer experience": 10,
        "Time-to-deploy": 10,
        "Request timeout": 10,
        "RAM disponible": 10,
    }

    if project_profile.get("streaming"):
        base_criteria["WebSocket/streaming"] = 15
    if project_profile.get("global_users"):
        base_criteria["Multi-region"] = 15
    if project_profile.get("team_size", 1) > 2:
        base_criteria["Preview environments"] = 10
    if project_profile.get("needs_database"):
        base_criteria["Databases integradas"] = 10
    if project_profile.get("enterprise"):
        base_criteria["Compliance/SLA"] = 15
    if project_profile.get("irregular_traffic"):
        base_criteria["Cold start"] = 10

    total = sum(base_criteria.values())
    normalized = {k: round(v / total * 100) for k, v in base_criteria.items()}

    remainder = 100 - sum(normalized.values())
    first_key = list(normalized.keys())[0]
    normalized[first_key] += remainder

    return normalized

Paso 2: Asignar pesos

Los pesos reflejan TUS prioridades, no "la verdad universal":

# Ejemplo: MVP de chatbot AI, 1 developer, presupuesto limitado
mvp_criteria = {
    "Coste mensual": 25,
    "Developer experience": 20,
    "Time-to-deploy": 15,
    "Request timeout": 15,
    "WebSocket/streaming": 15,
    "Cold start": 10,
}

# Ejemplo: App AI para empresa, equipo de 5, usuarios globales
enterprise_criteria = {
    "Escalabilidad": 20,
    "Multi-region": 15,
    "Compliance/SLA": 15,
    "RAM disponible": 10,
    "Databases integradas": 10,
    "Preview environments": 10,
    "CI/CD integration": 10,
    "Coste mensual": 10,
}

assert sum(mvp_criteria.values()) == 100
assert sum(enterprise_criteria.values()) == 100

Paso 3: Calcular scores ponderados

def calculate_platform_scores(
    criteria: dict, evaluations: dict
) -> dict:
    """Calcula scores ponderados para cada plataforma."""
    platforms = list(evaluations.keys())
    results = {}

    for platform in platforms:
        total = 0
        details = {}
        for criterion, weight in criteria.items():
            if criterion in evaluations[platform]:
                score, note = evaluations[platform][criterion]
                weighted = weight * score
                total += weighted
                details[criterion] = {
                    "score": score,
                    "weight": weight,
                    "weighted": weighted,
                    "note": note,
                }
        results[platform] = {"total": total, "details": details}

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

    return {"rankings": sorted_results, "max_possible": max_possible}


# Ejemplo con criterios de MVP
mvp_results = calculate_platform_scores(mvp_criteria, platform_scores)

print("=== Rankings para MVP AI Chatbot ===\n")
print(f"{'Platform':<25} {'Score':>6} {'%':>6}")
print("-" * 40)
for platform, data in mvp_results["rankings"].items():
    pct = data["total"] / mvp_results["max_possible"] * 100
    print(f"{platform:<25} {data['total']:>6} {pct:>5.1f}%")

Paso 4: Interpretar resultados

def interpret_results(results: dict) -> str:
    """Genera interpretación automática de los resultados."""
    rankings = list(results["rankings"].items())
    winner = rankings[0]
    runner_up = rankings[1]
    max_score = results["max_possible"]

    diff = winner[1]["total"] - runner_up[1]["total"]
    diff_pct = diff / max_score * 100

    interpretation = f"""
## Resultado

**Plataforma recomendada: {winner[0]}**
- Score: {winner[1]['total']}/{max_score} ({winner[1]['total']/max_score*100:.0f}%)

**Segunda opción: {runner_up[0]}**
- Score: {runner_up[1]['total']}/{max_score} ({runner_up[1]['total']/max_score*100:.0f}%)

**Diferencia: {diff_pct:.1f}%**
"""

    if diff_pct < 5:
        interpretation += """
⚠️ **Diferencia marginal (<5%).** Ambas plataformas son viables.
Considera factores cualitativos: familiaridad del equipo, preferencia de CLI vs dashboard.
"""
    elif diff_pct < 15:
        interpretation += """
La diferencia es moderada. La recomendación es clara pero la segunda
opción es viable si hay constraints no capturados en la matrix.
"""
    else:
        interpretation += """
La diferencia es significativa. La plataforma recomendada es claramente
superior para este caso de uso.
"""

    return interpretation

Escenarios Predefinidos

Escenario 1: MVP / Proyecto Personal

mvp_profile = {
    "team_size": 1,
    "budget": "$0-20/mes",
    "users": "<100",
    "traffic": "irregular",
    "streaming": False,
    "global_users": False,
    "enterprise": False,
}

mvp_weights = {
    "Coste mensual": 30,
    "Time-to-deploy": 25,
    "Developer experience": 20,
    "Cold start": 15,
    "RAM disponible": 10,
}

# Resultado típico: Fly.io o Railway
# Fly.io gana en coste (free tier más generoso)
# Railway gana en DX (más rápido de setup)

Escenario 2: Startup Early-Stage

startup_profile = {
    "team_size": 3,
    "budget": "$50-100/mes",
    "users": "500-5000",
    "traffic": "growing",
    "streaming": True,
    "global_users": False,
    "enterprise": False,
    "needs_database": True,
}

startup_weights = {
    "Developer experience": 20,
    "Databases integradas": 15,
    "WebSocket/streaming": 15,
    "Preview environments": 15,
    "Coste mensual": 15,
    "Request timeout": 10,
    "CI/CD integration": 10,
}

# Resultado típico: Railway
# Preview environments + add-ons + DX

Escenario 3: App AI con Usuarios Globales

global_profile = {
    "team_size": 5,
    "budget": "$200-500/mes",
    "users": "10000+",
    "traffic": "high, global",
    "streaming": True,
    "global_users": True,
    "enterprise": False,
}

global_weights = {
    "Multi-region": 25,
    "Escalabilidad": 20,
    "WebSocket/streaming": 15,
    "Request timeout": 10,
    "RAM disponible": 10,
    "CI/CD integration": 10,
    "Coste mensual": 10,
}

# Resultado típico: Fly.io o AWS
# Fly.io gana en multi-region + simplicidad
# AWS gana si necesitas SageMaker o compliance

Escenario 4: Enterprise / Compliance

enterprise_profile = {
    "team_size": 10,
    "budget": "$500+/mes",
    "users": "enterprise",
    "enterprise": True,
    "needs_database": True,
}

enterprise_weights = {
    "Compliance/SLA": 25,
    "Escalabilidad": 20,
    "Control": 15,
    "Databases integradas": 10,
    "Multi-region": 10,
    "CI/CD integration": 10,
    "RAM disponible": 10,
}

# Resultado típico: AWS
# Ninguna plataforma alternativa tiene SOC2/HIPAA/enterprise SLA

El Framework Completo: Cuándo Usar Cada Plataforma

Decision Tree

¿Necesitas compliance enterprise (SOC2, HIPAA, ISO)?
    ├── Sí → AWS
    └── No ↓

¿Necesitas GPU para inferencia local?
    ├── Sí → Servicios especializados (RunPod, Lambda Labs)
    └── No ↓

¿Tu categoría óptima (M1) es Serverless?
    ├── Sí → AWS Lambda
    └── No (Managed) ↓

¿Necesitas multi-region o edge deployment?
    ├── Sí → Fly.io
    └── No ↓

¿Equipos de 3+ developers con PRs frecuentes?
    ├── Sí → Railway (preview environments)
    └── No ↓

¿Prioridad: pricing predecible?
    ├── Sí → Render
    └── No ↓

¿Prioridad: velocidad de iteración?
    ├── Sí → Railway
    └── No ↓

¿Prioridad: coste mínimo?
    └── Fly.io

Resumen ejecutivo

PlataformaElige cuandoEvita cuando
AWSCompliance, scale enterprise, servicios específicos (SageMaker, Lambda@Edge)MVP, equipo pequeño, presupuesto <$50/mes
RenderSimplicidad máxima, pricing predecible, demosNecesitas streaming largo (timeout 30s), multi-region
RailwayMejor DX, equipos con PRs, prototipos rápidosPresupuesto muy limitado ($0), necesitas multi-region
Fly.ioMulti-region, coste mínimo, WebSocket nativoQuieres dashboard-first workflow, equipo no técnico

Sensitivity Analysis

¿Qué pasa si cambias los pesos?

Un buen framework de decisión sobrevive a variaciones en los pesos. Si cambiar un peso ±10 cambia la recomendación, la diferencia entre plataformas es marginal y deberías elegir por factores cualitativos.

def sensitivity_analysis(
    base_criteria: dict,
    evaluations: dict,
    vary_criterion: str,
    vary_range: range = range(-10, 15, 5),
) -> list:
    """Analiza cómo cambia el ganador al variar el peso de un criterio."""
    results = []

    for delta in vary_range:
        adjusted = base_criteria.copy()
        original = adjusted[vary_criterion]
        adjusted[vary_criterion] = max(0, original + delta)

        total = sum(adjusted.values())
        if total == 0:
            continue
        normalized = {k: round(v / total * 100) for k, v in adjusted.items()}

        scores = calculate_platform_scores(normalized, evaluations)
        winner = list(scores["rankings"].keys())[0]
        winner_score = list(scores["rankings"].values())[0]["total"]

        results.append({
            "delta": delta,
            "weight": max(0, original + delta),
            "winner": winner,
            "score": winner_score,
        })

    return results


# Ejemplo: ¿Qué pasa si el coste importa más/menos?
analysis = sensitivity_analysis(mvp_criteria, platform_scores, "Coste mensual")
for r in analysis:
    print(f"  Coste peso={r['weight']:>3}: Ganador = {r['winner']:<20} (score: {r['score']})")

Interpretación de sensitivity analysis

Si el ganador cambia con ±5 de peso:
    → La decisión es marginal. Elige por familiaridad del equipo.

Si el ganador cambia con ±10 de peso:
    → La decisión es robusta pero no aplastante. Documenta la segunda opción.

Si el ganador NO cambia con ±15 de peso:
    → La decisión es sólida. La plataforma recomendada es claramente superior.

Ejercicios Prácticos

Ejercicio 1: Construir tu decision matrix v2

Toma los criterios de esta cápsula, selecciona 7-10 relevantes para tu caso, asigna pesos, y calcula scores. Produce un ranking documentado.

Ver solución
# decision_matrix_v2.py

# 1. Define tu perfil
my_profile = {
    "project": "DocuSearch AI",
    "stage": "MVP → Growth",
    "team": 2,
    "budget": "$30-50/mes",
    "users_current": 150,
    "users_target": 500,
    "needs_streaming": True,
    "needs_database": True,
    "global_users": False,
}

# 2. Selecciona criterios y pesos (suman 100)
my_criteria = {
    "Coste mensual": 20,
    "Developer experience": 15,
    "Request timeout": 15,
    "WebSocket/streaming": 15,
    "Databases integradas": 10,
    "RAM disponible": 10,
    "Cold start": 10,
    "CI/CD integration": 5,
}
assert sum(my_criteria.values()) == 100

# 3. Evalúa (usa los datos de platform_scores arriba)
# ... (ejecuta calculate_platform_scores)

# 4. Genera el documento
print(f"""
# Decision Matrix v2: {my_profile['project']}
Fecha: 2026-03-08
Stage: {my_profile['stage']}
Team: {my_profile['team']} developers

## Criterios y Pesos
""")

for criterion, weight in sorted(my_criteria.items(), key=lambda x: -x[1]):
    print(f"| {criterion} | {weight} |")

# 5. Calcula y muestra resultados
results = calculate_platform_scores(my_criteria, platform_scores)
print("\n## Rankings")
for platform, data in results["rankings"].items():
    pct = data["total"] / results["max_possible"] * 100
    print(f"| {platform} | {data['total']} | {pct:.0f}% |")

Ejercicio 2: Sensitivity analysis de tu matrix

Ejecuta sensitivity analysis variando los 3 criterios con mayor peso. Determina si tu recomendación es robusta.

Ver solución
# sensitivity.py

top_3_criteria = sorted(my_criteria.items(), key=lambda x: -x[1])[:3]

print("=== Sensitivity Analysis ===\n")
for criterion, weight in top_3_criteria:
    print(f"\n--- Variando: {criterion} (base: {weight}) ---")
    analysis = sensitivity_analysis(
        my_criteria, platform_scores, criterion, range(-15, 20, 5)
    )

    changes = set()
    for r in analysis:
        changes.add(r["winner"])
        print(f"  peso={r['weight']:>3}: {r['winner']:<20} (score: {r['score']})")

    if len(changes) == 1:
        print(f"  → Resultado ESTABLE: siempre {list(changes)[0]}")
    else:
        print(f"  → Resultado SENSIBLE: cambia entre {', '.join(changes)}")

print("\n## Conclusión")
# Si los 3 criterios principales producen resultado estable:
# → Tu recomendación es sólida
# Si 1+ produce resultado sensible:
# → Documenta las condiciones bajo las cuales la recomendación cambia

Ejercicio 3: Comparar escenarios MVP vs Scale

Calcula la matrix para tu app en stage MVP Y en stage Scale. Documenta cómo cambia la recomendación cuando creces.

Ver solución
mvp_criteria = {
    "Coste mensual": 25,
    "Developer experience": 20,
    "Time-to-deploy": 20,
    "Request timeout": 15,
    "Cold start": 10,
    "WebSocket/streaming": 10,
}

scale_criteria = {
    "Escalabilidad": 25,
    "Multi-region": 15,
    "Compliance/SLA": 15,
    "RAM disponible": 10,
    "Databases integradas": 10,
    "CI/CD integration": 10,
    "Preview environments": 10,
    "Coste mensual": 5,
}

assert sum(mvp_criteria.values()) == 100
assert sum(scale_criteria.values()) == 100

mvp_results = calculate_platform_scores(mvp_criteria, platform_scores)
scale_results = calculate_platform_scores(scale_criteria, platform_scores)

print("=== MVP Stage ===")
for p, d in mvp_results["rankings"].items():
    print(f"  {p}: {d['total']}")

print("\n=== Scale Stage ===")
for p, d in scale_results["rankings"].items():
    print(f"  {p}: {d['total']}")

mvp_winner = list(mvp_results["rankings"].keys())[0]
scale_winner = list(scale_results["rankings"].keys())[0]

if mvp_winner != scale_winner:
    print(f"\n⚠️ La recomendación CAMBIA: {mvp_winner} (MVP) → {scale_winner} (Scale)")
    print("Documenta el migration path en tu decision matrix.")
else:
    print(f"\n✅ La recomendación es CONSISTENTE: {mvp_winner} para ambos stages")
## Resultado típico

MVP: Railway (DX + coste razonable)
Scale: AWS (compliance + escalabilidad + multi-region)

Migration path:
1. MVP → Growth: Mantener Railway, agregar Redis, optimizar queries
2. Growth → Scale: Migrar a AWS cuando compliance o multi-region sea requerido
3. Trigger: >5000 usuarios O requerimiento SOC2/HIPAA O factura >$200/mes

Ejercicio 4: Documento de decisión para tu equipo

Genera un documento de decisión completo (estilo ADR) que podrías compartir con tu equipo o incluir en la documentación del proyecto.

Ver solución
# ADR-001: Platform Selection for DocuSearch AI

## Status: Accepted
## Date: 2026-03-08
## Decision Makers: [Tu nombre]

## Context

DocuSearch AI es una API RAG que usa GPT-4o-mini para responder
preguntas sobre documentación técnica. Actualmente corre en Docker
Compose local. Necesitamos deployar a producción.

## Decision

**Plataforma elegida: Railway**

## Rationale

Decision matrix v2 con 8 criterios ponderados:
- Railway: 380/500 (76%)
- Fly.io: 355/500 (71%)
- Render: 330/500 (66%)
- AWS: 290/500 (58%)

Railway gana por:
1. Developer experience (CLI + preview environments)
2. Request timeout 5 min (suficiente para RAG + streaming)
3. Add-ons integrados (PostgreSQL + Redis con un click)
4. Coste razonable (~$25-30/mes)

## Alternatives Considered

- **Fly.io:** Más barato y multi-region, pero nuestros usuarios
  están todos en Latam. Multi-region no justifica la complejidad extra.
- **Render:** Más simple, pero timeout de 30s es limitante
  para queries RAG complejas.
- **AWS:** Demasiada complejidad para nuestro stage (MVP, equipo de 2).

## Consequences

- Deploy via `railway up` y auto-deploy en push a main
- CI/CD con GitHub Actions → staging → production
- PostgreSQL y Redis como add-ons de Railway
- Si crecemos >5000 usuarios o necesitamos compliance:
  re-evaluar con migration path a AWS

## Re-evaluation Triggers

- [ ] >5000 usuarios activos
- [ ] Factura Railway >$100/mes
- [ ] Requerimiento SOC2/HIPAA
- [ ] Necesidad de multi-region (usuarios globales)
- [ ] Review programado: 2026-06-08 (3 meses)

Troubleshooting

Problema 1: "Dos plataformas tienen scores muy cercanos (<5%)"

Solución: Ejecuta sensitivity analysis. Si la diferencia es marginal, elige por:

  1. Familiaridad del equipo (ya usaste Railway en un proyecto → Railway)
  2. Ecosistema (ya tienes PostgreSQL en Railway → quédate)
  3. Haz un deploy de prueba en ambas y elige por experiencia

Problema 2: "Mi matrix dice AWS pero no quiero la complejidad"

Solución: La matrix refleja tus pesos. Si tus pesos priorizan control y compliance, AWS gana. Si eso no es tu prioridad real, ajusta los pesos. La matrix es tan honesta como tus inputs.

Problema 3: "Mi caso no encaja en ningún escenario predefinido"

Solución: Los escenarios son puntos de partida, no respuestas finales. Crea tu propio perfil, selecciona criterios relevantes, y ajusta pesos. La matrix es un framework — tú la configuras.


Resumen

  • Decision matrix v2 extiende la v1 del M1 con criterios de plataforma: AWS vs Render vs Railway vs Fly.io.
  • Criterios AI-specific (request timeout, RAM, streaming, cold start) son los que más diferencian plataformas para AI workloads.
  • Los pesos reflejan TUS prioridades — MVP prioriza coste y DX, enterprise prioriza compliance y escalabilidad.
  • Sensitivity analysis valida si tu recomendación es robusta o marginal.
  • El stage del proyecto cambia la plataforma óptima: Railway para MVP → AWS para scale es un path común.
  • No hay "mejor plataforma universal" — hay mejor plataforma para TU caso, TUS constraints, TU stage.
  • Documenta la decisión como un ADR — tu futuro yo y tu equipo te lo agradecerán.

Recursos Adicionales

  1. Architecture Decision Records (ADR) — Formato estándar para documentar decisiones
  2. Decision Matrix — Wikipedia — Teoría de decision matrices
  3. Sensitivity Analysis — Investopedia — Fundamentos de sensitivity analysis
  4. Platform Engineering — CNCF — White paper sobre plataformas
  5. ThoughtWorks Technology Radar — Estado del arte en deployment platforms
  6. Cloud Provider Comparison — CloudOptimizer — Herramienta de comparación de providers