Platform Build vs. Buy Framework
Make informed build-versus-buy decisions for platform components. Covers total cost of ownership analysis, evaluation criteria, vendor lock-in assessment, hybrid approaches, and the decision framework that prevents both NIH syndrome and vendor dependency.
Every platform team faces the build-versus-buy decision repeatedly: should we build our own CI/CD pipeline or use GitHub Actions? Custom monitoring or Datadog? In-house feature flags or LaunchDarkly? The wrong choice in either direction is expensive — building everything creates maintenance burden, buying everything creates vendor lock-in.
Decision Framework
Step 1: Is this a core differentiator?
YES → Build (competitive advantage)
NO → Buy (commodity, don't reinvent)
Step 2: What is the Total Cost of Ownership?
Build Cost:
Engineering time to build × hourly rate
+ Ongoing maintenance (20% of build cost/year)
+ Opportunity cost (what else could engineers do?)
+ Hiring/retaining specialists
+ Bug fixes, security patches, upgrades
Buy Cost:
License/SaaS fee × years
+ Integration engineering
+ Customization/workarounds
+ Vendor lock-in migration cost (exit cost)
+ Scaling costs (per-seat, per-event pricing)
Step 3: Does a COTS solution meet 80% of needs?
YES → Buy, customize 20%
NO → Build or find a better vendor
Step 4: What is the switching cost?
LOW → Buy (easy to switch later)
HIGH → Build or ensure contract flexibility
Decision Matrix:
Build Buy
Core IP: ✓ Always ✗ Never
Commodity: ✗ Avoid ✓ Prefer
Custom workflow: ✓ Often ✗ Rarely fits
Scale concern: ✓ Control ~ Depends on pricing
Time-to-market: ✗ Slow ✓ Fast
Maintenance: ✗ Ongoing ✓ Vendor handles
Total Cost Analysis
class BuildVsBuyAnalysis:
"""Compare total cost of ownership for build vs. buy."""
def build_cost(self, params: dict) -> dict:
"""Calculate total cost of building in-house."""
build_engineers = params["engineers"]
build_months = params["build_months"]
hourly_rate = params["fully_loaded_hourly_rate"]
initial_build = (
build_engineers * build_months * 160 * hourly_rate
)
annual_maintenance = initial_build * 0.20
annual_opportunity = (
params.get("maintenance_engineers", 1) * 12 * 160 * hourly_rate
)
years = params.get("analysis_years", 3)
return {
"year_1": initial_build + annual_maintenance,
"year_2": annual_maintenance + annual_opportunity,
"year_3": annual_maintenance + annual_opportunity,
"total_3yr": (
initial_build +
(annual_maintenance * years) +
(annual_opportunity * (years - 1))
),
}
def buy_cost(self, params: dict) -> dict:
"""Calculate total cost of buying/licensing."""
annual_license = params["annual_license_fee"]
integration = params["integration_cost"]
years = params.get("analysis_years", 3)
return {
"year_1": annual_license + integration,
"year_2": annual_license,
"year_3": annual_license,
"total_3yr": (annual_license * years) + integration,
"exit_cost": params.get("migration_cost", 0),
}
Anti-Patterns
| Anti-Pattern | Consequence | Fix |
|---|---|---|
| NIH Syndrome (build everything) | Massive maintenance burden, slow velocity | Build only differentiators, buy commodity |
| Buy without exit strategy | Vendor lock-in, price increases | Evaluate switching cost, use abstraction layers |
| Compare build cost only to license fee | Miss maintenance, opportunity cost | TCO analysis including 3-year maintenance |
| One-time decision | Requirements change, market evolves | Reassess yearly, technology changes fast |
| All-or-nothing thinking | Miss hybrid approaches | Build core, buy supporting tools |
The build-versus-buy decision is not about capability — your team can probably build anything. It is about allocation — whether building this specific thing is the best use of your engineering time compared to everything else on the roadmap.