🏗️ What is Platform Engineering?

Platform engineering is the discipline of building and operating Internal Developer Platforms (IDPs) — self-service infrastructure and tooling that lets application developers ship faster without requiring deep Kubernetes knowledge. The platform team is the "team that serves teams."

Kubernetes is the foundation, but the IDP abstracts it. Developers interact with higher-level constructs (environments, services, pipelines) while the platform team maintains the underlying complexity.

Developer Experience Layer Backstage portal CLI (idp create service) Self-service UI Platform Abstractions (CRDs / Operators) Application CRD Environment CRD DatabaseClaim CRD Pipeline CRD Platform Services (running on K8s) ArgoCD cert-manager External Secrets Karpenter Prometheus+Grafana Harbor Kubernetes (the foundation — managed by platform team)

Reduce Cognitive Load

Developers shouldn't need to understand Ingress, ServiceAccounts, NetworkPolicies, and ResourceQuotas just to deploy an app. The platform encapsulates that knowledge.

Golden Paths

Pre-built, opinionated templates for common patterns: microservice, batch job, ML training. Easy to follow; hard to go wrong.

Self-Service

Teams provision environments, databases, and pipelines via a portal or CLI — no tickets to the platform team, no weeks of waiting.

Paved Road

The golden path bakes in observability, security, and cost controls by default. Following it means compliance is automatic.

🛤️ Golden Paths & Backstage

Golden Path — Microservice Template

A golden path is a Cookiecutter/Helm/Backstage template that generates a fully configured, production-ready service skeleton in one command. It encodes your organisation's standards.

# What a golden path microservice template generates:
my-service/
├── Dockerfile                    # multi-stage build, non-root user
├── .github/workflows/
│   └── build-push.yaml          # CI: test → build → sign → push to Harbor
├── helm/
│   └── my-service/
│       ├── Chart.yaml
│       └── values.yaml          # pre-configured: resources, probes, HPA
├── k8s/
│   ├── namespace.yaml           # with PodSecurity labels + quota
│   ├── service-account.yaml     # IRSA annotation
│   └── argocd-app.yaml          # ArgoCD Application pointing to this repo
└── backstage/
    └── catalog-info.yaml        # service registration in Backstage

Backstage — Service Catalog

Backstage is a CNCF incubating project (from Spotify) that provides a unified developer portal: service catalog, software templates, TechDocs, and plugin ecosystem.

# catalog-info.yaml — register a service in Backstage
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: payment-service
  description: Handles payment processing
  annotations:
    github.com/project-slug: my-org/payment-service
    backstage.io/techdocs-ref: dir:.
    grafana/dashboard-selector: "title=Payment Service"
    argocd/app-name: payment-service-prod
  tags: [payments, critical, java]
spec:
  type: service
  lifecycle: production
  owner: team-payments
  system: checkout-platform
  dependsOn:
    - component:fraud-service
    - resource:postgres-payments

Backstage Software Template

# Software template — "Create a new microservice" in Backstage UI
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: microservice-template
  title: New Microservice (Golden Path)
  description: Creates a production-ready microservice with CI/CD, monitoring, and ArgoCD
spec:
  owner: platform-team
  type: service
  parameters:
    - title: Service Details
      properties:
        serviceName:
          type: string
          description: Name of the service (lowercase, kebab-case)
        owner:
          type: string
          description: Owning team
          ui:field: OwnerPicker
        language:
          type: string
          enum: [go, java, python, node]
  steps:
    - id: fetch-template
      action: fetch:template
      input:
        url: ./skeleton
        values:
          serviceName: ${{ parameters.serviceName }}
          owner: ${{ parameters.owner }}
    - id: publish
      action: publish:github
      input:
        repoUrl: github.com?repo=${{ parameters.serviceName }}&owner=my-org
    - id: register
      action: catalog:register
      input:
        repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }}
        catalogInfoPath: /backstage/catalog-info.yaml
💡 Backstage reduces "day 1" friction dramatically A developer clicks "Create New Service" in Backstage, fills out a form, and gets a repo with a working CI pipeline, Helm chart, ArgoCD app, and monitoring dashboard — already deployed to staging. Total time: 5 minutes instead of 2 weeks.

🔌 Platform Abstractions with Operators

Platform teams build custom CRDs and operators that provide high-level abstractions over raw Kubernetes primitives. Developers interact with Application and Environment objects — the operator expands them into Deployments, Services, Ingresses, HPA, PDB, and all the other scaffolding.

Custom Application CRD

# Developer writes this — 30 lines instead of 300
apiVersion: platform.example.com/v1
kind: Application
metadata:
  name: payment-service
  namespace: team-payments
spec:
  image: harbor.example.com/payments/payment-service:v2.1.0
  replicas:
    min: 2
    max: 20
    targetCPU: 70
  port: 8080
  resources:
    cpu: "500m"
    memory: "512Mi"
  ingress:
    host: payments.example.com
    tls: true
  env:
    - name: DB_HOST
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: host
  healthCheck:
    path: /health
    port: 8080

The Application Operator reconciles this into:

  • Deployment with resource requests, liveness/readiness probes, securityContext
  • Service + Ingress with TLS (cert-manager annotation auto-added)
  • HorizontalPodAutoscaler (min/max/targetCPU)
  • PodDisruptionBudget (minAvailable: 1 auto-set)
  • ServiceMonitor for Prometheus scraping
  • NetworkPolicy (default-deny + allow ingress-controller ingress)

Environment CRD — Ephemeral Preview Environments

# Create a full preview environment from a PR
apiVersion: platform.example.com/v1
kind: Environment
metadata:
  name: pr-1234
  namespace: previews
  annotations:
    platform.example.com/pr: "1234"
    platform.example.com/ttl: "48h"   # auto-delete after 48h
spec:
  source:
    repo: my-org/payment-service
    branch: feature/new-checkout
  services:
    - name: payment-service
      image: harbor.example.com/payments/payment-service:pr-1234
    - name: postgres
      type: ephemeral-database     # operator provisions a temp DB
  domain: pr-1234.preview.example.com
ℹ️ Crossplane + custom compositions = database self-service Combine Crossplane's CompositeResource with a platform CRD so developers can request kind: PostgreSQLDatabase and the platform provisions the RDS instance, injects credentials via ESO, and configures network access — all without a ticket.

Platform Tools Ecosystem

CapabilityTool(s)What it provides
Developer portalBackstageService catalog, templates, docs, plugin ecosystem
GitOps deliveryArgoCD / FluxApp deployment from Git, multi-cluster sync
Infrastructure provisioningCrossplane, TerraformSelf-service cloud resources (DBs, buckets, queues)
Secret managementESO + Vault / AWS SMAuto-inject secrets, rotation, no Git secrets
ObservabilityPrometheus + Loki + TempoAuto-configured for every golden-path service
Policy enforcementKyverno / OPA GatekeeperGuardrails baked into golden path by default
Cost visibilityKubecost / OpenCostPer-team cost chargeback, waste alerts
Preview environmentsCustom operator + ArgoCDEphemeral per-PR namespaces, auto-cleanup

👥 Platform Team Operating Model

Team Topologies — Platform as Product

The platform team is a Enabling Team or Platform Team in the Team Topologies model. They treat the IDP as an internal product — with a roadmap, SLAs, user research, and regular developer surveys. The key shift: the platform team serves developers, not the other way round.

Treat it as a Product

The IDP has a product manager, a roadmap, versioned releases, and a changelog. Developers are customers. Run NPS surveys quarterly.

Measure Developer Velocity

Track DORA metrics: Deployment Frequency, Lead Time for Change, Change Failure Rate, MTTR. The platform's job is to improve all four.

Office Hours

Weekly platform office hours where developers can ask questions, report friction, and suggest improvements. Prevents ticket-driven bottlenecks.

Thin Platform Layer

Resist the urge to build everything. Compose existing tools (ArgoCD, cert-manager, ESO) rather than rebuilding them. Add glue, not replacement.

DORA Metrics — How to Measure Platform Impact

MetricElite benchmarkHow platform helps
Deployment FrequencyMultiple per dayGolden path with automated CI/CD + ArgoCD reduces deployment friction
Lead Time for Change< 1 hourPreview environments, automated testing, one-click deploy
Change Failure Rate< 5%Canary releases, automated rollback, policy guardrails
MTTR< 1 hourCentralised observability, runbooks in Backstage, on-call tooling

IDP Maturity Model

Level 1 — Manual

  • kubectl apply by hand
  • Shared kubeconfig
  • No templates
  • Tickets for everything

Level 2 — Automated

  • CI/CD pipelines
  • GitOps (ArgoCD/Flux)
  • Basic RBAC
  • Shared observability

Level 3 — Self-Service

  • Golden path templates
  • Backstage portal
  • Self-service envs
  • Cost visibility

Level 4 — Platform Product

  • Custom CRDs/operators
  • Internal SLAs
  • DORA tracking
  • Developer NPS program
⚠️ Don't jump straight to Level 4 Many teams try to build a full IDP from scratch before solving basic automation. Start at Level 2 (reliable CI/CD + GitOps), then add self-service once the foundation is solid. A complex IDP built on shaky foundations is worse than no IDP.

📝 Knowledge Check

Q1. A developer needs to deploy a new microservice. In an organisation with a mature IDP at Level 3–4 maturity, what is their typical experience?
  • A) Open a ticket to the platform team and wait 2 weeks for namespace provisioning
  • B) Clone a golden path template, fill in a form in Backstage, and get a repo + CI/CD + ArgoCD app + monitoring in minutes
  • C) Manually write Deployment, Service, Ingress, HPA, PDB, and ServiceMonitor YAMLs from scratch
  • D) Request kubectl cluster-admin access to deploy directly
B) Golden path via Backstage. A mature IDP eliminates "day 1" friction entirely. The developer interacts with a software template that scaffolds the entire service — repo, CI, Helm chart, ArgoCD app, observability, and Backstage catalog entry — all pre-configured to organisation standards. The developer focuses on business logic, not platform complexity.
Q2. What is the primary benefit of a custom Application CRD that expands into Deployment + HPA + PDB + NetworkPolicy?
  • A) It reduces the number of Kubernetes API calls needed
  • B) It hides platform complexity from developers while enforcing organisational standards automatically in every deployment
  • C) It improves pod scheduling performance
  • D) It allows developers to bypass RBAC policies
B) Hides complexity + enforces standards. The custom CRD gives developers a simple, opinionated interface (30 lines instead of 300). More importantly, the operator that expands it always generates correct security contexts, resource limits, PDBs, and monitoring — developers can't accidentally skip them. Standards are enforced by construction, not by code review.
Q3. Which DORA metric most directly measures the impact of reducing deployment friction via golden paths and automated CI/CD?
  • A) Change Failure Rate — fewer configuration errors
  • B) MTTR — faster recovery from incidents
  • C) Deployment Frequency — teams deploy more often when it's easy and safe
  • D) Lead Time for Change — time from commit to production
C) Deployment Frequency — though D is also strongly impacted. When deploying is safe, automated, and takes minutes rather than days, teams deploy more frequently (multiple times per day instead of once per month). Deployment Frequency is the clearest signal of reduced friction. Lead Time for Change also improves dramatically as manual steps are eliminated from the pipeline.