Platform Engineering in 2026: Building an Internal Developer Platform That Teams Actually Use



Platform Engineering in 2026: Building an Internal Developer Platform That Teams Actually Use

The DevOps movement told developers: “you build it, you run it.” That was transformative — and also, for many teams, a disaster. Engineers spending 40% of their time on infrastructure, compliance, security configs, and CI/CD debugging is not what “you build it, you run it” was supposed to mean.

Platform Engineering is the answer: build an Internal Developer Platform (IDP) that abstracts the complexity away, so product teams can self-service their infrastructure needs without becoming Kubernetes experts. Done right, it’s the highest-leverage investment a platform team can make. Done wrong, it becomes an under-used portal that nobody trusts.

This post is about doing it right.

Modern office infrastructure representing developer platform and tooling Photo by Growtika on Unsplash


What Is an Internal Developer Platform?

An IDP is the sum of tools, services, and workflows that your platform team provides so product engineers can:

  • Deploy applications without writing Kubernetes YAML
  • Provision databases, queues, and caches without filing tickets
  • Get observability (logs, metrics, traces) automatically
  • Pass security and compliance checks without becoming security experts
  • Understand their service’s health, dependencies, and ownership at a glance

The core concept is the Golden Path — the paved road that’s so easy and good that teams choose it voluntarily. Not a mandate; an offer they can’t refuse.


The CNCF Platform Maturity Model

The CNCF’s Platform Engineering working group defines four maturity levels:

LevelNameCharacteristics
1ProvisionalAd-hoc tools, no formal platform team
2OperationalShared tooling, some self-service
3ScalableSelf-service portal, product thinking applied to platform
4OptimizingPlatform as a product, SLOs, continuous improvement loop

Most organizations in 2026 are at Level 2 trying to reach Level 3. The jump from 2 to 3 is the hardest: it requires platform teams to adopt product management practices, not just engineering practices.


The Core Components

A modern IDP in 2026 typically consists of:

┌─────────────────────────────────────────────────────────┐
│              Developer Portal (Backstage)                │
│  Service Catalog | Docs | Templates | Dependency Map    │
└─────────────────────────────────┬───────────────────────┘
                                  │
              ┌───────────────────┼───────────────────┐
              ↓                   ↓                   ↓
┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐
│   Application   │  │  Infrastructure │  │   Observability  │
│   Delivery      │  │  Self-Service   │  │   Platform       │
│                 │  │                 │  │                  │
│ • ArgoCD        │  │ • Crossplane    │  │ • Prometheus     │
│ • Tekton/GHA    │  │ • Terraform CDK │  │ • Grafana        │
│ • OCI Registry  │  │ • PostgreSQL    │  │ • Jaeger/Tempo   │
│ • Helm/Kustomize│  │ • Redis, S3     │  │ • OpenTelemetry  │
└─────────────────┘  └─────────────────┘  └─────────────────┘
              │
┌─────────────────────────────────────────────────────────┐
│              Security & Policy Layer                     │
│   OPA/Kyverno | Vault | RBAC | Audit Logging            │
└─────────────────────────────────────────────────────────┘

Setting Up Backstage: The Developer Portal

Backstage (Spotify → CNCF) has become the de facto developer portal. Here’s a production setup:

Bootstrap

npx @backstage/create-app@latest --name my-platform
cd my-platform
yarn install

Service Catalog: The Foundation

Every service in your organization should register in Backstage via a catalog-info.yaml in the repo root:

# catalog-info.yaml (lives in each service repo)
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: payment-service
  description: Handles payment processing for all checkout flows
  annotations:
    github.com/project-slug: myorg/payment-service
    backstage.io/techdocs-ref: dir:.
    grafana/dashboard-selector: "payment-service"
    opsgenie.com/component-selector: payment-service
  tags:
    - node
    - payments
    - pci-dss
  links:
    - url: https://grafana.internal/d/payment-service
      title: Grafana Dashboard
      icon: dashboard
    - url: https://runbooks.internal/payment-service
      title: Runbooks
      icon: docs
spec:
  type: service
  lifecycle: production
  owner: payments-team
  system: checkout
  dependsOn:
    - component:order-service
    - resource:payments-postgres
    - resource:stripe-api
  providesApis:
    - payment-api

Software Templates: Self-Service Service Creation

This is where the magic happens. Teams create new services via Backstage without touching infrastructure:

# templates/nodejs-service/template.yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: nodejs-microservice
  title: Node.js Microservice
  description: Creates a production-ready Node.js microservice with CI/CD, observability, and database
  tags:
    - node
    - microservice
    - 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-]*[a-z0-9]$'
          description: "Lowercase, hyphen-separated (e.g., user-profile-service)"
        description:
          title: Description
          type: string
          ui:widget: textarea
        owner:
          title: Owner Team
          type: string
          ui:field: OwnerPicker
          ui:options:
            catalogFilter:
              kind: Group

    - title: Infrastructure
      properties:
        database:
          title: Database
          type: string
          default: none
          enum: [none, postgres, mysql]
        cache:
          title: Cache
          type: boolean
          default: false
          description: Provision a Redis instance

    - title: Repository
      required: [repoUrl]
      properties:
        repoUrl:
          title: Repository Location
          type: string
          ui:field: RepoUrlPicker
          ui:options:
            allowedHosts: [github.com]

  steps:
    - id: fetch-template
      name: Fetch Template
      action: fetch:template
      input:
        url: ./skeleton
        values:
          name: $
          description: $
          owner: $
          database: $
          cache: $

    - id: publish
      name: Publish to GitHub
      action: publish:github
      input:
        allowedHosts: [github.com]
        description: $
        repoUrl: $
        defaultBranch: main
        repoVisibility: internal

    - id: provision-infra
      name: Provision Infrastructure
      action: http:backstage:request
      input:
        method: POST
        path: /api/platform/provision
        body:
          serviceName: $
          database: $
          cache: $
          team: $

    - id: register
      name: Register in Catalog
      action: catalog:register
      input:
        repoContentsUrl: $
        catalogInfoPath: /catalog-info.yaml

  output:
    links:
      - title: Repository
        url: $
      - title: Open in Catalog
        url: $

From the developer’s perspective: fill in a form, click “Create,” and 3 minutes later they have a GitHub repo with CI/CD, a Kubernetes namespace, an optional database, RBAC, and their service registered in the catalog. No tickets. No waiting.


Infrastructure Self-Service with Crossplane

Crossplane turns your Kubernetes cluster into a universal infrastructure control plane. Platform teams define CompositeResourceDefinitions; product teams claim resources:

# Platform team defines: PostgresDatabase composite resource
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xpostgresdatabases.platform.myorg.com
spec:
  group: platform.myorg.com
  names:
    kind: XPostgresDatabase
    plural: xpostgresdatabases
  claimNames:
    kind: PostgresDatabase
    plural: postgresdatabases
  versions:
    - name: v1alpha1
      served: true
      referenceable: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                size:
                  type: string
                  enum: [small, medium, large]
                  default: small
                  description: "small=2CPU/4GB, medium=4CPU/16GB, large=8CPU/32GB"
                region:
                  type: string
                  default: ap-northeast-2
# Product team uses: just a claim
apiVersion: platform.myorg.com/v1alpha1
kind: PostgresDatabase
metadata:
  name: payment-service-db
  namespace: payments
spec:
  size: medium
  region: ap-northeast-2
  writeConnectionSecretToRef:
    name: payment-db-credentials

That’s it. The product team gets a managed PostgreSQL database in AWS RDS, with backups, monitoring, and credentials injected into their namespace — without knowing anything about Terraform or AWS IAM.


Measuring Platform Success

The most important metric for a platform team: Cognitive Load Reduction.

# Key platform metrics to track

PLATFORM_METRICS = {
    # Developer Experience
    "time_to_first_deployment": "Time from repo creation to first prod deploy (target: <1 day)",
    "self_service_ratio": "% of infra requests fulfilled without tickets (target: >80%)",
    "golden_path_adoption": "% of services using recommended templates (target: >70%)",

    # Operational
    "mttr": "Mean time to restore after incidents (platform-caused)",
    "platform_slo_compliance": "% of platform components meeting their SLOs",
    "deployment_frequency": "Deployments per team per day",

    # Cost
    "cost_per_deployment": "Infrastructure cost / total deployments",
    "idle_resource_ratio": "% of provisioned resources actively used",
}

Collect these via developer surveys (quarterly NPS/developer satisfaction), platform telemetry, and DORA metrics.

Teams collaborating around screens representing developer productivity and platform engineering Photo by Annie Spratt on Unsplash


Common Pitfalls and How to Avoid Them

1. Building for the platform team, not for developers Fix: Treat platform as a product. Run user research with developers. Ship based on their pain points, not your engineering interests.

2. The “big bang” portal launch Fix: Start with one golden path for your most common service type. Get 10 teams using it. Iterate. Then expand.

3. Mandating adoption Fix: Pave the road, don’t mandate it. If your golden path is better, teams will use it. If they don’t, your golden path needs work.

4. Platform team as gatekeeper Fix: Self-service or bust. If teams have to file tickets to use your platform, you’ve built a bureaucracy, not a platform.

5. No SLOs for the platform itself Fix: Your platform is production infrastructure. Treat it that way. Define and publish SLOs. Measure. Improve.


Key Takeaways

  • Platform Engineering is DevOps evolved — the answer to “you build it, you run it” scaling problems
  • The Golden Path is the core value proposition: self-service, well-lit, safe by default
  • Backstage is the standard developer portal; software templates are its highest-value feature
  • Crossplane enables infrastructure self-service via Kubernetes-native APIs
  • Measure developer experience (NPS, time-to-deploy, self-service ratio) not just infrastructure metrics
  • A platform team should have product managers, not just engineers
  • Adoption is earned, not mandated — if developers don’t use your golden path, it’s not good enough yet

The best platform teams in 2026 are invisible. Developers don’t think about CI/CD, Kubernetes, or databases. They think about product features. That’s the goal.


References: CNCF Platform Engineering, Backstage.io Documentation, Crossplane Documentation, Team Topologies (Book)

이 글이 도움이 되셨다면 공감 및 광고 클릭을 부탁드립니다 :)