Every engineering organization above a certain size develops the same invisible problem. Some teams ship confidently and fast, deploying multiple times a day, spinning up new services without friction, debugging production issues with full observability. Other teams spend a disproportionate amount of time navigating infrastructure, waiting for platform approvals, reinventing tooling that already exists somewhere else in the organization, or simply not knowing what the right approach is.
The difference between those teams is rarely talent. It is almost always platform.
An Internal Developer Platform (IDP) is the curated set of tools, workflows, standards, and self-service capabilities that an organization provides to its engineering teams to accelerate and standardize how software gets built and delivered. When done well, it creates a golden path, a paved road of sensible defaults that makes doing the right thing the easy thing, without mandating it.
This guide covers how to design and build an IDP that actually gets adopted, from the layers of a platform to service catalog design, self-service provisioning, developer portals, and the feedback loops that make a platform evolve rather than stagnate.
The Platform Engineering Problem
The instinct that creates IDPs is sound, instead of every team solving the same infrastructure, CI/CD, observability, and security problems independently, a dedicated platform team solves them once and makes the solution available to everyone.
The execution often fails for two reasons:
Building what the platform team thinks developers need instead of what developers actually need. A platform built on assumptions rather than feedback becomes a set of tools nobody uses, maintained by a team that wonders why adoption is low.
Building a wall, not a road. An IDP that forces developers through approval processes, ticket queues, and bureaucratic gates in the name of standardization creates friction rather than removing it. Developers route around friction, they find workarounds, create shadow IT, and build their own tooling rather than use a platform that slows them down.
The golden path metaphor captures the right design intent, a golden path is not a mandatory route. It is the path that is so clearly well-maintained, well-lit, and well-documented that choosing it is easier than going off-road. Developers take it because it is the better option, not because they have no alternative.
The Layers of an Internal Developer Platform
An IDP is not a single tool, it is a stack of integrated capabilities that collectively reduce the cognitive load of building and operating software:
┌─────────────────────────────────────────────────────────┐
│ Developer Portal │
│ (Service Catalog, Docs, Templates, Dashboards) │
└─────────────────────┬───────────────────────────────────┘
│
┌─────────────────────▼───────────────────────────────────┐
│ Self-Service Layer │
│ (Provisioning, Environments, Pipelines, Secrets) │
└─────────────────────┬───────────────────────────────────┘
│
┌─────────────────────▼───────────────────────────────────┐
│ Orchestration Layer │
│ (Kubernetes, ArgoCD, Helm, Spacelift, Vault) │
└─────────────────────┬───────────────────────────────────┘
│
┌─────────────────────▼───────────────────────────────────┐
│ Infrastructure Layer │
│ (AWS/GCP/Azure, Terraform, Networking) │
└─────────────────────────────────────────────────────────┘
Infrastructure layer - the cloud resources, networks, and compute that everything runs on. Platform engineers own this entirely, application teams never interact with it directly.
Orchestration layer - the tools that manage how applications are packaged, deployed, and operated. Kubernetes, ArgoCD, Helm, Vault, and Spacelift live here. Application teams interact with this layer through abstracted interfaces, not directly.
Self-service layer - the APIs, CLIs, and forms that allow application teams to provision environments, spin up new services, manage secrets, and trigger deployments without opening tickets or waiting for platform team approval.
Developer portal - the unified interface where developers discover what the platform provides, browse available services, read documentation, access templates, and monitor the health of their systems.
Step 1 - Start with Developer Research, Not Technology
The most common IDP failure mode is building the platform before understanding what slows developers down. Before choosing any technology, spend four weeks doing structured developer research.
Platform Research Interview Guide
Onboarding
- How long did it take you to deploy your first change to production?
- What was the hardest part of understanding how deployment works here?
Day-to-Day Development
- What takes longer than it should?
- What do you find yourself doing manually that you wish was automated?
- What do you copy from one service to another when starting something new?
Production Operations
- When something breaks in production, what information do you reach for first?
- How confident are you in your ability to diagnose and fix a production incident?
Collaboration with Platform
- How do you currently request infrastructure or tooling changes?
- How long does it typically take to get what you need?
- What would make your relationship with the platform team more effective?
Run this interview with 8-10 engineers across different teams and tenure levels. The patterns that emerge, the repeated friction points, the consistent gaps, the workarounds that everyone has independently invented, are your platform backlog. Build against that backlog, not against a preconceived vision of what a modern platform looks like.
Step 2 - Define Your Golden Path
A golden path is a specific, opinionated recommendation for how to build and operate a service at your organization. It is not a mandate, it is the answer to "if I'm starting something new, what should I use?".Document your golden path explicitly. e.g:
Golden Path: New Backend Service
Language and Framework
- Recommended: TypeScript with NestJS
- Why: Consistent with 80% of existing services, best internal support.
Packaging and Deployment
- Recommended: Docker → Helm chart → ArgoCD
- Template: github.com/your-org/service-template
Database
- Recommended: PostgreSQL (managed RDS via Terraform module)
-
Self-service: Use the
rds-postgresmodule in the platform registry
Observability
- Required: Structured logging (Pino), OpenTelemetry tracing, health check endpoint
- Dashboard template: Auto-provisioned on service creation
Secrets Management
- Recommended: External Secrets Operator + Vault
- Never: Hardcoded credentials or plaintext .env files in Git
CI/CD
- Recommended: GitHub Actions with the platform's standard workflow templates
- Templates: Available in platform/ci-templates
Testing Requirements
- Unit test coverage ≥ 70%
- Contract tests for all service-to-service dependencies
- E2E tests for critical user journeys
The golden path document is a living artifact, it evolves as the platform evolves and as teams provide feedback on what works and what doesn't. Version-control it and treat updates as pull requests with engineering review.
Step 3 - Service Templates and Scaffolding
The first concrete experience most developers have with the platform is creating a new service. If that experience requires reading 15 pages of documentation and making 40 manual configuration decisions, the platform has failed at the moment of first contact.
Backstage software templates (or equivalent) solve this by making new service creation a form-filling exercise that produces a fully configured repository:
# backstage/templates/nestjs-service/template.yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: nestjs-backend-service
title: NestJS Backend Service
description: Production-ready NestJS service with observability, CI/CD, and Helm chart
tags: [typescript, nestjs, backend, recommended]
spec:
owner: platform-team
type: service
parameters:
- title: Service Details
required: [name, description, owner]
properties:
name:
title: Service Name
type: string
pattern: '^[a-z][a-z0-9-]*$'
description: Lowercase, hyphenated (e.g. orders-service)
description:
title: Description
type: string
owner:
title: Owning Team
type: string
ui:field: OwnerPicker
ui:options:
catalogFilter:
kind: Group
- title: Infrastructure
properties:
database:
title: PostgreSQL Database
type: boolean
default: false
redis:
title: Redis Cache
type: boolean
default: false
initialReplicas:
title: Initial Replica Count
type: integer
default: 2
minimum: 1
maximum: 10
steps:
- id: fetch-template
name: Fetch Service Template
action: fetch:template
input:
url: ./skeleton
values:
name: ${{ parameters.name }}
description: ${{ parameters.description }}
owner: ${{ parameters.owner }}
database: ${{ parameters.database }}
redis: ${{ parameters.redis }}
- id: create-repo
name: Create GitHub Repository
action: publish:github
input:
repoUrl: github.com?repo=${{ parameters.name }}&owner=your-org
description: ${{ parameters.description }}
defaultBranch: main
repoVisibility: private
topics: [${{ parameters.owner }}, backend, nestjs]
- id: register
name: Register in Catalog
action: catalog:register
input:
repoContentsUrl: ${{ steps['create-repo'].output.repoContentsUrl }}
catalogInfoPath: /catalog-info.yaml
output:
links:
- title: Repository
url: ${{ steps['create-repo'].output.remoteUrl }}
- title: Open in Catalog
icon: catalog
entityRef: ${{ steps['register'].output.entityRef }}
The scaffold output is a complete repository with: NestJS boilerplate, Dockerfile, Helm chart, GitHub Actions CI workflow, catalog-info.yaml for Backstage registration, OpenTelemetry instrumentation, structured logging configuration, and a health check endpoint. A developer fills out a form and receives a production-ready repository in under two minutes.
Step 4 - The Developer Portal with Backstage
Backstage (open-sourced by Spotify) is the most widely adopted developer portal framework. It provides the unified interface layer of your IDP, service catalog, documentation, templates, and plugin integrations:
# catalog-info.yaml — every service registers itself
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: orders-service
description: Order management and fulfilment service
annotations:
github.com/project-slug: your-org/orders-service
grafana/dashboard-selector: "title=Orders Service"
pagerduty.com/service-id: P1234AB
argocd/app-name: orders-service-production
sonarqube.org/project-key: your-org_orders-service
tags: [backend, typescript, nestjs]
links:
- url: https://orders.your-domain.com/api/docs
title: API Documentation
- url: https://runbooks.internal/orders-service
title: Runbook
spec:
type: service
lifecycle: production
owner: backend-team
system: order-management
dependsOn:
- component:users-service
- component:payments-service
providesApis:
- orders-api
With every service registered, Backstage provides:
- Service catalog - searchable inventory of every service, its owner, its status, and its dependencies
- TechDocs - docs-as-code rendered from Markdown in each service's repository
- Dependency graph - visual map of which services depend on the other
- Plugin integrations - ArgoCD deployment status, Grafana dashboards, SonarQube code quality, PagerDuty incidents, GitHub pull requests, all surfaced in a single UI per service
The principle: a developer should be able to open Backstage, find their service, and immediately see its deployment status, recent incidents, code quality score, current pull requests, and documentation, without navigating five different tools.
Step 5 - Self-Service Environment Provisioning
One of the highest-friction developer experiences is getting a new environment. In organizations without self-service, this means a ticket to the platform team, a wait of days to weeks, and a result that may or may not match what was requested.
Self-service provisioning eliminates the ticket. Using Spacelift, Terraform modules, and Backstage actions, a developer can provision a complete environment by selecting options from a catalog:
# Backstage action - provision environment from catalog
- id: provision-environment
name: Provision Development Environment
action: platform:provision-environment
input:
name: ${{ parameters.environmentName }}
team: ${{ parameters.owner }}
services:
- name: orders-service
version: ${{ parameters.ordersVersion }}
- name: users-service
version: latest
database: true
expiresIn: 7d # auto-destroy after 7 days to control costs
Behind the scenes, this triggers a Spacelift stack that applies a Terraform configuration, provisions an EKS namespace, deploys the specified service versions via ArgoCD, and returns a URL to the environment, all within minutes, with no platform team involvement.
Ephemeral environments that auto-expire prevent environment sprawl and the associated cost. Developers get exactly the environment they need, for exactly as long as they need it, without creating permanent infrastructure debt.
Step 6 - Platform Observability and DORA Metrics
A platform team that cannot measure its impact cannot improve it. Track DORA metrics (Developer Research and Assessment) as the primary signal of platform effectiveness:
| Metric | Definition | Target (Elite) |
|---|---|---|
| Deployment Frequency | How often code deploys to production | Multiple times per day |
| Lead Time for Changes | Commit to production time | Less than one hour |
| Change Failure Rate | Percentage of deployments causing incidents | Less than 5% |
| Failed Deployment Recovery Time | Time to restore after a failure | Less than one hour |
Instrument these metrics from your CI/CD pipeline and present them in Backstage per team:
// platform/metrics/dora-collector.ts
export async function collectDeploymentFrequency(
team: string,
windowDays: number = 30
): Promise<number> {
const deployments = await githubClient.actions.listWorkflowRuns({
owner: 'your-org',
repo: '*', // across all repos for this team
status: 'success',
created: `>${subDays(new Date(), windowDays).toISOString()}`,
});
return deployments.data.total_count / windowDays; // deployments per day
}
Beyond DORA, track platform-specific adoption metrics:
- Golden path adoption rate - percentage of services using the standard template
- Self-service provisioning rate - percentage of environment requests fulfilled via self-service vs. tickets
- Platform NPS - developer satisfaction score from quarterly surveys
- Time to first deployment - for new engineers, from day one to first production deployment
These metrics tell you whether the platform is achieving its purpose, reducing developer friction and increasing engineering velocity.
Step 7 - The Platform Team Operating Model
A platform is a product. It needs a product owner, a backlog, a roadmap, and a feedback mechanism. The most common failure of platform engineering is treating it as an infrastructure team that happens to build tools, rather than a product team whose customers are developers.
Treat developers as customers. Platform teams should maintain a developer experience feedback channel (Slack, GitHub Discussions, regular office hours) and respond to feedback with the same urgency they'd apply to a production incident. A developer who can't get a question answered about the platform will build around it.
Ship incrementally. An IDP does not need to be complete before it launches. A golden path with a service template, a basic CI/CD workflow, and a simple developer portal is more valuable than a comprehensive platform that's six months from delivery. Ship the minimum viable platform, measure adoption, and iterate.
Dogfood your own platform. Every internal tool the platform team builds should run on the platform. If the platform team bypasses its own standards, using different deployment mechanisms, different observability tools, different secret management, it signals that the platform isn't good enough even for the people who built it.
Establish an API for the platform itself. The self-service layer should be programmable, accessible via CLI, GitHub Actions, and API, not just a web UI. Developers who can script the platform can build their own workflows on top of it, multiplying the platform's reach beyond what the platform team alone can provide.
Common Pitfalls to Avoid
Building for the ideal developer, not the actual developer. A platform that assumes developers know Kubernetes, understand Terraform, and have read all the documentation will be used only by developers who already don't need it. Design the golden path for the median engineer on your team, not the most experienced.
Deprecating old paths before the golden path is ready. Removing existing tools and workflows before self-service alternatives exist forces developers to wait rather than incentivizing them to adopt. Always build the new path first, then migrate, then deprecate.
Measuring adoption instead of outcomes. High adoption of the platform is not the goal, high developer velocity and high system reliability are the goals. If the platform is widely adopted but DORA metrics aren't improving, the platform is being used but not delivering value.
Under-resourcing the platform team. A platform serving 100 developers with a team of one is a support ticket queue with a nice interface. Platform teams typically need one platform engineer for every 10–15 application engineers to be effective, depending on scope and maturity.
Conclusion
An Internal Developer Platform is not a one-time project. It is a product that evolves continuously in response to how developers needs change as the organization scales. The golden path is not a fixed route it is a living recommendation that reflects the organization's current best understanding of how to build reliable software efficiently.
The investment is significant. A platform team, a developer portal, service templates, self-service provisioning, and DORA metric tracking require sustained engineering effort and organizational commitment. The return on that investment is compounding, every developer who doesn't spend two days setting up a new service, every incident that gets diagnosed in minutes instead of hours, every security practice that is adopted automatically rather than manually, represents developer time recovered and directed toward building the product.
The platform is never finished. But a platform that is thoughtfully built, honestly measured, and continuously improved is one of the highest-leverage investments a growing engineering organization can make.
What does your team's current "golden path" look like, documented or de facto? And what's the single biggest friction point it hasn't yet solved? Share in the comments.
Top comments (0)