DEV Community

ThankGod Chibugwum Obobo
ThankGod Chibugwum Obobo

Posted on Originally published at actocodes.hashnode.dev

Internal Developer Platforms: How to Build a Golden Path for Your Engineering Team

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)          │
└─────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

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-postgres module 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 }}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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.

PlatformEngineering #InternalDeveloperPlatform #Backstage #DeveloperExperience #DevOps

Top comments (0)