Module 7: Alternative Platforms (Render, Railway, Fly.io)

7. Platform Decision Matrix: The Complete Framework

Description

In this capsule you'll extend the Module 1 decision matrix with platform criteria. In M1 you evaluated categories (Local, Serverless, Managed, Self-hosted). Here you evaluate concrete platforms within the "Managed" category: AWS vs Render vs Railway vs Fly.io. The result is the decision matrix v2 — a complete framework that maps your project's requirements to the optimal platform, with real data from the previous capsules.

Context: You've deployed on Render, Railway and Fly.io. You've compared features, pricing, limitations (capsule 05). You've integrated CI/CD (capsule 06). Now you have first-hand experience with each platform. The decision matrix v2 formalizes that experience into a reusable framework. Every time you start a new project, this matrix tells you where to deploy.


From Categories to Platforms

Recap: Decision Matrix v1 (Module 1)

In M1 your matrix evaluated categories:

Matrix v1 (M1):
Criteria × [Local, Serverless, Managed, Self-hosted]
→ Result: "For my case, Managed is the optimal category"

That tells you the direction, but not the platform. "Managed" includes Render, Railway, Fly.io, Heroku, DigitalOcean App Platform, Google Cloud Run, Azure Container Apps... You need a second level of decision.

Decision Matrix v2: Platforms

Matrix v2 (M7):
Criteria × [AWS, Render, Railway, Fly.io]
→ Result: "For my case, Railway is the optimal platform"

Complete framework:
Matrix v1 → Category → Matrix v2 → Platform → Deploy

v2 doesn't replace v1 — it extends it. If your v1 says "Serverless," your platform is AWS Lambda (the only option in this guide). If it says "Managed," v2 chooses between Render, Railway and Fly.io. If it says "Local," you don't need a cloud platform. v2 only applies when the optimal category is Managed or when you want to compare Managed vs AWS.


Platform Criteria for AI

Standard criteria (from M1, adapted)

#CriterionDescriptionAI relevance
1Monthly costInfra + APIs + opsHigh — LLM APIs dominate, but infra matters
2Developer experienceSetup, CLI, workflows, logsHigh — fast iteration = better product
3Time-to-deployFrom commit to productionMedium — matters more in an MVP than at scale
4ScalabilityAbility to grow without migratingMedium — depends on the stage
5ControlAccess to configuration, debuggingLow for managed, high for enterprise

AI-specific criteria (new in v2)

#CriterionDescriptionWhy it's AI-specific
6Request timeoutMaximum time for an HTTP requestAI inference can take 5-30s
7Available RAMMaximum service memoryEmbeddings, vectors, in-memory models
8WebSocket/streamingSupport for token streamingChat UX requires streaming
9Cold startStartup time after inactivityAffects UX in apps with irregular traffic
10Multi-regionDeploy across multiple locationsGlobal latency for apps with distributed users

Team/business criteria

#CriterionDescriptionWhen it matters
11Vendor lock-inDifficulty of migrating to another platformAlways — but more at scale
12Preview environmentsTemporary deploys per PRTeams of 3+ developers
13Integrated databasesPostgreSQL, Redis as add-onsWhen your app needs persistence
14CI/CD integrationEase of connecting with GitHub ActionsTeams with existing CI/CD
15Compliance/SLAUptime guarantees, certificationsEnterprise, regulated data

You don't need every criterion. Select 7-10 that are relevant to your case.


Platform Evaluation: Real Data

Quantitative evaluation (1-5)

# Evaluation based on real data from capsules 02-06

platform_scores = {
    "AWS (Lambda + S3)": {
        "Monthly cost":      (3, "Pay-per-use, but setup + monitoring has a hidden cost"),
        "Developer experience": (2, "IAM, API Gateway, CloudWatch — steep learning curve"),
        "Time-to-deploy":     (1, "30-60 min first time, 5-10 min redeploy"),
        "Scalability":        (5, "Native auto-scaling, practically unlimited"),
        "Control":            (5, "Granular configuration of everything"),
        "Request timeout":    (5, "Lambda: 15 min maximum"),
        "Available RAM":      (4, "Lambda: up to 10 GB. EC2: unlimited"),
        "WebSocket/streaming": (3, "API Gateway WebSocket exists but is complex"),
        "Cold start":         (2, "Lambda cold start: 1-10s, worse with container images"),
        "Multi-region":       (5, "Native with configuration"),
        "Vendor lock-in":     (1, "High — IAM, API Gateway, CloudWatch, Lambda specifics"),
        "Preview environments": (2, "Manual — stages in API Gateway or separate stacks"),
        "Integrated databases": (4, "RDS, ElastiCache, DynamoDB — all managed"),
        "CI/CD integration":  (4, "Native CodePipeline, GitHub Actions with the AWS CLI"),
        "Compliance/SLA":     (5, "SOC2, HIPAA, ISO, 99.99% SLA"),
    },
    "Render": {
        "Monthly cost":      (4, "Predictable: $7-85/month per service"),
        "Developer experience": (3, "Simple dashboard, but no CLI"),
        "Time-to-deploy":     (4, "3-5 min first time, auto-deploy on push"),
        "Scalability":        (3, "Horizontal scaling, but single region"),
        "Control":            (2, "Limited — fixed plans, few tuning options"),
        "Request timeout":    (2, "30 seconds — limiting for AI"),
        "Available RAM":      (3, "512 MB - 8 GB depending on plan"),
        "WebSocket/streaming": (3, "SSE yes, WebSocket only on Starter+"),
        "Cold start":         (2, "Free: 15-45s. Starter+: 0"),
        "Multi-region":       (1, "4 regions, but single region per service"),
        "Vendor lock-in":     (4, "Low — Docker container + simple render.yaml"),
        "Preview environments": (2, "In beta, not mature"),
        "Integrated databases": (3, "Managed PostgreSQL and Redis"),
        "CI/CD integration":  (3, "Native auto-deploy, API for GitHub Actions"),
        "Compliance/SLA":     (2, "Basic — no SOC2, limited SLA"),
    },
    "Railway": {
        "Monthly cost":      (3, "Pay-per-use: ~$25-30/month for a typical AI app"),
        "Developer experience": (5, "Excellent CLI, preview envs, one-click add-ons"),
        "Time-to-deploy":     (5, "2-3 min with the CLI, the fastest"),
        "Scalability":        (3, "Auto-scaling on Pro, but single region"),
        "Control":            (3, "More than Render, less than AWS"),
        "Request timeout":    (4, "5 minutes — enough for most AI"),
        "Available RAM":      (4, "Up to 32 GB on Pro"),
        "WebSocket/streaming": (4, "Full WebSocket support"),
        "Cold start":         (3, "2-8s with sleep enabled"),
        "Multi-region":       (1, "Single region"),
        "Vendor lock-in":     (4, "Low — Docker + minimal railway.toml"),
        "Preview environments": (5, "Native, automatic, with isolated DB per PR"),
        "Integrated databases": (5, "PostgreSQL, Redis, MySQL, MongoDB with one click"),
        "CI/CD integration":  (4, "Auto-deploy + CLI in GitHub Actions"),
        "Compliance/SLA":     (2, "Basic — Pro has an SLA but limited"),
    },
    "Fly.io": {
        "Monthly cost":      (5, "The cheapest: ~$10-15/month for an AI app"),
        "Developer experience": (3, "Powerful CLI but more initial setup"),
        "Time-to-deploy":     (3, "5-7 min first time, 2-3 min redeploy"),
        "Scalability":        (5, "Native multi-region, auto-start/stop"),
        "Control":            (4, "SSH to VMs, Machines API, granular configuration"),
        "Request timeout":    (4, "Configurable — no hard limit"),
        "Available RAM":      (3, "256 MB - 16 GB+"),
        "WebSocket/streaming": (5, "Full support, native"),
        "Cold start":         (4, "Firecracker: 300ms-2s"),
        "Multi-region":       (5, "30+ regions, anycast routing"),
        "Vendor lock-in":     (3, "Medium — fly.toml, volumes, multi-region config"),
        "Preview environments": (2, "Manual with the Machines API"),
        "Integrated databases": (3, "Fly Postgres + Upstash Redis"),
        "CI/CD integration":  (4, "Official action + flyctl in CI"),
        "Compliance/SLA":     (3, "Better than Render/Railway, but not enterprise"),
    },
}

Building Your Decision Matrix v2

Step 1: Select relevant criteria

Not all criteria apply to your case. Select 7-10:

def select_criteria(project_profile: dict) -> dict:
    """Selects criteria and weights based on the project profile."""

    base_criteria = {
        "Monthly cost": 15,
        "Developer experience": 10,
        "Time-to-deploy": 10,
        "Request timeout": 10,
        "Available RAM": 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["Integrated databases"] = 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

Step 2: Assign weights

The weights reflect YOUR priorities, not "the universal truth":

# Example: AI chatbot MVP, 1 developer, limited budget
mvp_criteria = {
    "Monthly cost": 25,
    "Developer experience": 20,
    "Time-to-deploy": 15,
    "Request timeout": 15,
    "WebSocket/streaming": 15,
    "Cold start": 10,
}

# Example: AI app for a company, team of 5, global users
enterprise_criteria = {
    "Scalability": 20,
    "Multi-region": 15,
    "Compliance/SLA": 15,
    "Available RAM": 10,
    "Integrated databases": 10,
    "Preview environments": 10,
    "CI/CD integration": 10,
    "Monthly cost": 10,
}

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

Step 3: Calculate weighted scores

def calculate_platform_scores(
    criteria: dict, evaluations: dict
) -> dict:
    """Calculates weighted scores for each platform."""
    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}


# Example with MVP criteria
mvp_results = calculate_platform_scores(mvp_criteria, platform_scores)

print("=== Rankings for 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}%")

Step 4: Interpret results

def interpret_results(results: dict) -> str:
    """Generates an automatic interpretation of the results."""
    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"""
## Result

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

**Second option: {runner_up[0]}**
- Score: {runner_up[1]['total']}/{max_score} ({runner_up[1]['total']/max_score*100:.0f}%)

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

    if diff_pct < 5:
        interpretation += """
⚠️ **Marginal difference (<5%).** Both platforms are viable.
Consider qualitative factors: team familiarity, CLI vs dashboard preference.
"""
    elif diff_pct < 15:
        interpretation += """
The difference is moderate. The recommendation is clear but the second
option is viable if there are constraints not captured in the matrix.
"""
    else:
        interpretation += """
The difference is significant. The recommended platform is clearly
superior for this use case.
"""

    return interpretation

Predefined Scenarios

Scenario 1: MVP / Personal Project

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

mvp_weights = {
    "Monthly cost": 30,
    "Time-to-deploy": 25,
    "Developer experience": 20,
    "Cold start": 15,
    "Available RAM": 10,
}

# Typical result: Fly.io or Railway
# Fly.io wins on cost (more generous free tier)
# Railway wins on DX (faster to set up)

Scenario 2: Early-Stage Startup

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

startup_weights = {
    "Developer experience": 20,
    "Integrated databases": 15,
    "WebSocket/streaming": 15,
    "Preview environments": 15,
    "Monthly cost": 15,
    "Request timeout": 10,
    "CI/CD integration": 10,
}

# Typical result: Railway
# Preview environments + add-ons + DX

Scenario 3: AI App with Global Users

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

global_weights = {
    "Multi-region": 25,
    "Scalability": 20,
    "WebSocket/streaming": 15,
    "Request timeout": 10,
    "Available RAM": 10,
    "CI/CD integration": 10,
    "Monthly cost": 10,
}

# Typical result: Fly.io or AWS
# Fly.io wins on multi-region + simplicity
# AWS wins if you need SageMaker or compliance

Scenario 4: Enterprise / Compliance

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

enterprise_weights = {
    "Compliance/SLA": 25,
    "Scalability": 20,
    "Control": 15,
    "Integrated databases": 10,
    "Multi-region": 10,
    "CI/CD integration": 10,
    "Available RAM": 10,
}

# Typical result: AWS
# No alternative platform has SOC2/HIPAA/enterprise SLA

The Complete Framework: When to Use Each Platform

Decision Tree

Do you need enterprise compliance (SOC2, HIPAA, ISO)?
    ├── Yes → AWS
    └── No ↓

Do you need a GPU for local inference?
    ├── Yes → Specialized services (RunPod, Lambda Labs)
    └── No ↓

Is your optimal category (M1) Serverless?
    ├── Yes → AWS Lambda
    └── No (Managed) ↓

Do you need multi-region or edge deployment?
    ├── Yes → Fly.io
    └── No ↓

Teams of 3+ developers with frequent PRs?
    ├── Yes → Railway (preview environments)
    └── No ↓

Priority: predictable pricing?
    ├── Yes → Render
    └── No ↓

Priority: iteration speed?
    ├── Yes → Railway
    └── No ↓

Priority: minimum cost?
    └── Fly.io

Executive summary

PlatformChoose whenAvoid when
AWSCompliance, enterprise scale, specific services (SageMaker, Lambda@Edge)MVP, small team, budget <$50/month
RenderMaximum simplicity, predictable pricing, demosYou need long streaming (30s timeout), multi-region
RailwayBest DX, teams with PRs, fast prototypesVery limited budget ($0), you need multi-region
Fly.ioMulti-region, minimum cost, native WebSocketYou want a dashboard-first workflow, non-technical team

Sensitivity Analysis

What happens if you change the weights?

A good decision framework survives variations in the weights. If changing a weight by ±10 changes the recommendation, the difference between platforms is marginal and you should choose based on qualitative factors.

def sensitivity_analysis(
    base_criteria: dict,
    evaluations: dict,
    vary_criterion: str,
    vary_range: range = range(-10, 15, 5),
) -> list:
    """Analyzes how the winner changes as a criterion's weight varies."""
    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


# Example: what happens if cost matters more/less?
analysis = sensitivity_analysis(mvp_criteria, platform_scores, "Monthly cost")
for r in analysis:
    print(f"  Cost weight={r['weight']:>3}: Winner = {r['winner']:<20} (score: {r['score']})")

Interpreting the sensitivity analysis

If the winner changes with ±5 in weight:
    → The decision is marginal. Choose by team familiarity.

If the winner changes with ±10 in weight:
    → The decision is robust but not overwhelming. Document the second option.

If the winner does NOT change with ±15 in weight:
    → The decision is solid. The recommended platform is clearly superior.

Hands-On Exercises

Exercise 1: Build your decision matrix v2

Take this capsule's criteria, select 7-10 relevant to your case, assign weights, and calculate scores. Produce a documented ranking.

See solution
# decision_matrix_v2.py

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

# 2. Select criteria and weights (sum to 100)
my_criteria = {
    "Monthly cost": 20,
    "Developer experience": 15,
    "Request timeout": 15,
    "WebSocket/streaming": 15,
    "Integrated databases": 10,
    "Available RAM": 10,
    "Cold start": 10,
    "CI/CD integration": 5,
}
assert sum(my_criteria.values()) == 100

# 3. Evaluate (use the data from platform_scores above)
# ... (run calculate_platform_scores)

# 4. Generate the document
print(f"""
# Decision Matrix v2: {my_profile['project']}
Date: 2026-03-08
Stage: {my_profile['stage']}
Team: {my_profile['team']} developers

## Criteria and Weights
""")

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

# 5. Calculate and show results
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}% |")

Exercise 2: Sensitivity analysis of your matrix

Run a sensitivity analysis varying the 3 highest-weighted criteria. Determine whether your recommendation is robust.

See solution
# 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--- Varying: {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"  weight={r['weight']:>3}: {r['winner']:<20} (score: {r['score']})")

    if len(changes) == 1:
        print(f"  → STABLE result: always {list(changes)[0]}")
    else:
        print(f"  → SENSITIVE result: changes between {', '.join(changes)}")

print("\n## Conclusion")
# If the 3 main criteria produce a stable result:
# → Your recommendation is solid
# If 1+ produces a sensitive result:
# → Document the conditions under which the recommendation changes

Exercise 3: Compare MVP vs Scale scenarios

Calculate the matrix for your app at the MVP stage AND at the Scale stage. Document how the recommendation changes as you grow.

See solution
mvp_criteria = {
    "Monthly cost": 25,
    "Developer experience": 20,
    "Time-to-deploy": 20,
    "Request timeout": 15,
    "Cold start": 10,
    "WebSocket/streaming": 10,
}

scale_criteria = {
    "Scalability": 25,
    "Multi-region": 15,
    "Compliance/SLA": 15,
    "Available RAM": 10,
    "Integrated databases": 10,
    "CI/CD integration": 10,
    "Preview environments": 10,
    "Monthly cost": 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⚠️ The recommendation CHANGES: {mvp_winner} (MVP) → {scale_winner} (Scale)")
    print("Document the migration path in your decision matrix.")
else:
    print(f"\n✅ The recommendation is CONSISTENT: {mvp_winner} for both stages")
## Typical result

MVP: Railway (DX + reasonable cost)
Scale: AWS (compliance + scalability + multi-region)

Migration path:
1. MVP → Growth: Keep Railway, add Redis, optimize queries
2. Growth → Scale: Migrate to AWS when compliance or multi-region is required
3. Trigger: >5000 users OR SOC2/HIPAA requirement OR bill >$200/month

Exercise 4: Decision document for your team

Generate a complete decision document (ADR style) that you could share with your team or include in the project documentation.

See solution
# ADR-001: Platform Selection for DocuSearch AI

## Status: Accepted
## Date: 2026-03-08
## Decision Makers: [Your name]

## Context

DocuSearch AI is a RAG API that uses GPT-4o-mini to answer
questions about technical documentation. It currently runs on local
Docker Compose. We need to deploy to production.

## Decision

**Chosen platform: Railway**

## Rationale

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

Railway wins because of:
1. Developer experience (CLI + preview environments)
2. 5 min request timeout (enough for RAG + streaming)
3. Integrated add-ons (PostgreSQL + Redis with one click)
4. Reasonable cost (~$25-30/month)

## Alternatives Considered

- **Fly.io:** Cheaper and multi-region, but our users
  are all in Latam. Multi-region doesn't justify the extra complexity.
- **Render:** Simpler, but the 30s timeout is limiting
  for complex RAG queries.
- **AWS:** Too much complexity for our stage (MVP, team of 2).

## Consequences

- Deploy via `railway up` and auto-deploy on push to main
- CI/CD with GitHub Actions → staging → production
- PostgreSQL and Redis as Railway add-ons
- If we grow >5000 users or need compliance:
  re-evaluate with a migration path to AWS

## Re-evaluation Triggers

- [ ] >5000 active users
- [ ] Railway bill >$100/month
- [ ] SOC2/HIPAA requirement
- [ ] Need for multi-region (global users)
- [ ] Scheduled review: 2026-06-08 (3 months)

Troubleshooting

Problem 1: "Two platforms have very close scores (<5%)"

Solution: Run a sensitivity analysis. If the difference is marginal, choose by:

  1. Team familiarity (you already used Railway on a project → Railway)
  2. Ecosystem (you already have PostgreSQL on Railway → stay)
  3. Do a test deploy on both and choose by experience

Problem 2: "My matrix says AWS but I don't want the complexity"

Solution: The matrix reflects your weights. If your weights prioritize control and compliance, AWS wins. If that's not your real priority, adjust the weights. The matrix is only as honest as your inputs.

Problem 3: "My case doesn't fit any predefined scenario"

Solution: The scenarios are starting points, not final answers. Create your own profile, select relevant criteria, and adjust weights. The matrix is a framework — you configure it.


Summary

  • Decision matrix v2 extends the v1 from M1 with platform criteria: AWS vs Render vs Railway vs Fly.io.
  • AI-specific criteria (request timeout, RAM, streaming, cold start) are the ones that most differentiate platforms for AI workloads.
  • The weights reflect YOUR priorities — an MVP prioritizes cost and DX, enterprise prioritizes compliance and scalability.
  • Sensitivity analysis validates whether your recommendation is robust or marginal.
  • The project stage changes the optimal platform: Railway for MVP → AWS for scale is a common path.
  • There's no "universal best platform" — there's a best platform for YOUR case, YOUR constraints, YOUR stage.
  • Document the decision as an ADR — your future self and your team will thank you.

Additional Resources

  1. Architecture Decision Records (ADR) — Standard format for documenting decisions
  2. Decision Matrix — Wikipedia — Decision matrix theory
  3. Sensitivity Analysis — Investopedia — Sensitivity analysis fundamentals
  4. Platform Engineering — CNCF — White paper on platforms
  5. ThoughtWorks Technology Radar — State of the art in deployment platforms
  6. Cloud Provider Comparison — CloudOptimizer — Provider comparison tool