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)
| # | Criterion | Description | AI relevance |
|---|---|---|---|
| 1 | Monthly cost | Infra + APIs + ops | High — LLM APIs dominate, but infra matters |
| 2 | Developer experience | Setup, CLI, workflows, logs | High — fast iteration = better product |
| 3 | Time-to-deploy | From commit to production | Medium — matters more in an MVP than at scale |
| 4 | Scalability | Ability to grow without migrating | Medium — depends on the stage |
| 5 | Control | Access to configuration, debugging | Low for managed, high for enterprise |
AI-specific criteria (new in v2)
| # | Criterion | Description | Why it's AI-specific |
|---|---|---|---|
| 6 | Request timeout | Maximum time for an HTTP request | AI inference can take 5-30s |
| 7 | Available RAM | Maximum service memory | Embeddings, vectors, in-memory models |
| 8 | WebSocket/streaming | Support for token streaming | Chat UX requires streaming |
| 9 | Cold start | Startup time after inactivity | Affects UX in apps with irregular traffic |
| 10 | Multi-region | Deploy across multiple locations | Global latency for apps with distributed users |
Team/business criteria
| # | Criterion | Description | When it matters |
|---|---|---|---|
| 11 | Vendor lock-in | Difficulty of migrating to another platform | Always — but more at scale |
| 12 | Preview environments | Temporary deploys per PR | Teams of 3+ developers |
| 13 | Integrated databases | PostgreSQL, Redis as add-ons | When your app needs persistence |
| 14 | CI/CD integration | Ease of connecting with GitHub Actions | Teams with existing CI/CD |
| 15 | Compliance/SLA | Uptime guarantees, certifications | Enterprise, 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
| Platform | Choose when | Avoid when |
|---|---|---|
| AWS | Compliance, enterprise scale, specific services (SageMaker, Lambda@Edge) | MVP, small team, budget <$50/month |
| Render | Maximum simplicity, predictable pricing, demos | You need long streaming (30s timeout), multi-region |
| Railway | Best DX, teams with PRs, fast prototypes | Very limited budget ($0), you need multi-region |
| Fly.io | Multi-region, minimum cost, native WebSocket | You 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:
- Team familiarity (you already used Railway on a project → Railway)
- Ecosystem (you already have PostgreSQL on Railway → stay)
- 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
- Architecture Decision Records (ADR) — Standard format for documenting decisions
- Decision Matrix — Wikipedia — Decision matrix theory
- Sensitivity Analysis — Investopedia — Sensitivity analysis fundamentals
- Platform Engineering — CNCF — White paper on platforms
- ThoughtWorks Technology Radar — State of the art in deployment platforms
- Cloud Provider Comparison — CloudOptimizer — Provider comparison tool