Internal Developer Platforms Teams Actually Use
Design internal developer platforms that speed delivery: self-service infrastructure, golden paths, platform APIs, DX metrics and adoption that earns trust.

Why Most Internal Platforms Fail
Internal developer platforms fail when they are built by a platform team in isolation and then mandated onto product teams. Engineers route around tools that slow them down. A successful platform earns adoption by making the right thing the easiest thing.
Golden Paths, Not Golden Cages
A golden path is the recommended way to build, deploy, and operate a service. It handles the boring parts—CI/CD, observability, security scanning—so teams focus on business logic.
// ❌ Every team invents their own CI/CD, Dockerfile, monitoring setup
// Result: 15 teams, 15 different deployment processes, no shared tooling
// ✅ Platform provides a service template that includes everything
interface ServiceTemplate {
name: string;
language: "typescript" | "python" | "go";
includes: string[];
configurable: string[];
}
const goldenPath: ServiceTemplate = {
name: "standard-api-service",
language: "typescript",
includes: [
"Dockerfile with multi-stage build",
"CI/CD pipeline (build, test, deploy)",
"Health check endpoints (/health, /ready)",
"Structured logging (JSON, correlation IDs)",
"Prometheus metrics endpoint",
"OpenTelemetry tracing",
"Database migration runner",
"Secret management integration",
"Security scanning in CI",
],
configurable: [
"Service name",
"Port",
"Database type (Postgres, none)",
"Authentication (JWT, API key, none)",
"Deployment targets (staging, production)",
],
};
// Teams scaffold a new service in one command
// platform create-service --template standard-api-service --name order-apiSelf-Service Infrastructure
Waiting days for infrastructure tickets destroys flow. Expose infrastructure provisioning through APIs that product teams call directly.
interface InfrastructureRequest {
type: "database" | "cache" | "queue" | "storage";
environment: "development" | "staging" | "production";
config: Record<string, unknown>;
owner: string;
costCenter: string;
}
interface InfrastructureResponse {
id: string;
status: "provisioning" | "ready" | "failed";
connectionDetails: Record<string, string>;
estimatedReadyAt: string;
}
// Platform API — teams provision resources without tickets
class InfrastructureAPI {
async provision(
request: InfrastructureRequest
): Promise<InfrastructureResponse> {
// Validate against guardrails
this.validateGuardrails(request);
// Provision through IaC (Terraform, Pulumi, etc.)
const resource = await this.provisioningEngine.create({
type: request.type,
environment: request.environment,
config: this.applyDefaults(request.config, request.type),
tags: {
owner: request.owner,
"cost-center": request.costCenter,
"managed-by": "platform",
},
});
return {
id: resource.id,
status: "provisioning",
connectionDetails: resource.connectionDetails,
estimatedReadyAt: resource.estimatedReady,
};
}
private validateGuardrails(request: InfrastructureRequest): void {
const rules: Record<string, GuardrailCheck> = {
database: {
maxInstances: 3,
allowedSizes: ["small", "medium"],
requiresApproval: request.environment === "production",
},
cache: {
maxInstances: 5,
allowedSizes: ["small", "medium", "large"],
requiresApproval: false,
},
};
const rule = rules[request.type];
if (rule?.requiresApproval) {
throw new Error(
`Production ${request.type} requires approval — ` +
`submit via platform review queue`
);
}
}
}Developer Experience Metrics
Measure what matters: how long it takes to go from code to production, how often deployments fail, and how quickly developers can get started.
interface DeveloperMetrics {
// Lead time: commit to production
leadTimeP50: string;
leadTimeP90: string;
// Onboarding: first commit to first production deploy
onboardingTimeP50: string;
// Self-service: % of infra requests fulfilled without tickets
selfServiceRate: number;
// Deployment: success rate and rollback frequency
deploymentSuccessRate: number;
rollbackRate: number;
// Satisfaction: developer NPS or survey scores
developerSatisfactionScore: number;
}
function trackDeploymentMetrics(deployment: Deployment): void {
const leadTime = deployment.productionAt - deployment.committedAt;
metrics.histogram("deployment.lead_time_seconds", leadTime / 1000, {
service: deployment.service,
team: deployment.team,
});
metrics.counter("deployments.total", 1, {
service: deployment.service,
result: deployment.success ? "success" : "failure",
});
if (deployment.isRollback) {
metrics.counter("deployments.rollbacks", 1, {
service: deployment.service,
reason: deployment.rollbackReason,
});
}
}Platform as a Product
Treat the platform like a product with users, feedback loops, and iterative improvement. Product teams are your customers.
// Feedback collection built into the platform CLI
interface PlatformFeedback {
command: string; // What they were trying to do
outcome: "success" | "failure" | "abandoned";
frictionPoints: string[];
suggestion?: string;
timestamp: number;
}
// After each platform interaction, optionally collect feedback
async function collectFeedback(
command: string,
outcome: "success" | "failure" | "abandoned"
): Promise<void> {
// Only ask 10% of the time to avoid fatigue
if (Math.random() > 0.1) return;
const response = await prompt(
"Quick feedback: any friction with this command? (skip to dismiss)"
);
if (response) {
await platformAPI.submitFeedback({
command,
outcome,
frictionPoints: [response],
timestamp: Date.now(),
});
}
}
// Weekly platform team review
interface WeeklyReview {
topFrictionPoints: string[];
featureRequests: string[];
adoptionTrends: {
service: string;
usingGoldenPath: boolean;
lastDeployment: string;
}[];
actionItems: string[];
}Incremental Adoption Strategy
Mandating platform adoption breeds resentment. Instead, make the platform so useful that teams adopt it voluntarily.
// ❌ Big-bang migration — mandate all teams switch by Q3
// Result: teams rush, cut corners, blame the platform for every issue
// ✅ Incremental adoption — platform earns its way team by team
interface AdoptionPlan {
phase: string;
strategy: string;
successCriteria: string;
}
const adoptionPhases: AdoptionPlan[] = [
{
phase: "Seed",
strategy: "Partner with 1-2 friendly teams. Co-develop " +
"the golden path based on their real workflows.",
successCriteria: "Partner teams deploy faster than before " +
"and recommend the platform to others.",
},
{
phase: "Grow",
strategy: "Publish templates, docs, and success stories. " +
"Offer migration help. Never mandate.",
successCriteria: "50% of new services use the golden path. " +
"Existing teams start migrating voluntarily.",
},
{
phase: "Scale",
strategy: "Standardize on the platform for all new services. " +
"Provide migration tooling for legacy services.",
successCriteria: "80% of services on platform. Lead time " +
"reduced by 60% org-wide.",
},
{
phase: "Sustain",
strategy: "Continuous feedback and iteration. Platform team " +
"embedded with product teams on rotation.",
successCriteria: "Developer satisfaction stays above 4/5. " +
"Platform evolves with team needs.",
},
];Documentation as a Platform Feature
Undocumented platforms are unusable platforms. Documentation is not an afterthought—it is a core feature.
// Auto-generate docs from platform API definitions
interface PlatformDoc {
command: string;
description: string;
examples: { scenario: string; command: string; output: string }[];
prerequisites: string[];
troubleshooting: { problem: string; solution: string }[];
}
const createServiceDoc: PlatformDoc = {
command: "platform create-service",
description: "Scaffold a new service with CI/CD, monitoring, and deployment",
examples: [
{
scenario: "Create a TypeScript API service",
command: "platform create-service --name order-api --template standard-api",
output: "Created order-api in ./order-api with CI/CD pipeline configured",
},
{
scenario: "Create a service with a PostgreSQL database",
command: "platform create-service --name user-api --template standard-api --database postgres",
output: "Created user-api with PostgreSQL provisioned in development environment",
},
],
prerequisites: ["Platform CLI installed", "Authenticated via platform login"],
troubleshooting: [
{
problem: "Error: insufficient permissions",
solution: "Run 'platform login' to refresh authentication",
},
{
problem: "Error: template not found",
solution: "Run 'platform templates list' to see available templates",
},
],
};Key Takeaways
Internal developer platforms succeed when they treat developers as customers, not captive users. Build golden paths that handle CI/CD, observability, and security so teams can focus on business logic. Expose self-service infrastructure APIs so no one waits for tickets to get a database.
Measure developer experience with lead time, deployment success rate, and onboarding time—not just uptime. Adopt incrementally: partner with friendly teams, co-develop the platform based on real workflows, and let success stories drive organic adoption. The best platform is one that teams choose to use because it genuinely makes them faster, not one they are forced onto by organizational mandate.


