Platform engineering is the hottest trend in DevOps, and half the implementations out there are shelfware. The team builds an elaborate internal platform, presents it at an all-hands, and six months later developers are still SSHing into servers and running manual scripts.
The difference between platforms that get adopted and platforms that collect dust is simple: successful platforms start with developer pain, not infrastructure elegance.
What Developers Actually Want
Surveying the engineering team before building anything typically reveals the same pattern. The top pain points aren't about Kubernetes or CI/CD — they're about cognitive load:
- "Nobody knows which service owns this API endpoint" — service discovery
- "Setting up a new service takes two weeks of tickets" — scaffolding
- "The runbook for this alert is nowhere to be found" — documentation discovery
- "There's no easy way to tell if a service is healthy in production" — observability access
These are the problems worth solving first.
The Backstage Foundation
Backstage works well as the foundation — not because it's the best tool, but because it's the most extensible. The software catalog is the core:
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: payment-service
description: Handles payment processing via Stripe
annotations:
github.com/project-slug: myorg/payment-service
backstage.io/techdocs-ref: dir:.
pagerduty.com/service-id: P1234AB
grafana/dashboard-selector: "payment-service"
tags:
- python
- payments
- critical
links:
- url: https://grafana.internal/d/payments
title: Dashboard
icon: dashboard
spec:
type: service
lifecycle: production
owner: team-payments
system: checkout
providesApis:
- payment-api
consumesApis:
- user-api
- notification-api
dependsOn:
- resource:postgres-payments
- resource:redis-cacheEvery service gets a catalog-info.yml in its repo. Backstage automatically discovers and indexes them. The result: a single place where any developer can find who owns what, what depends on what, and where to look when something breaks.
Golden Paths: Self-Service Scaffolding
The highest-impact feature is software templates — golden paths that let developers create new services without filing tickets:
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: new-microservice
title: Create a New Microservice
description: Scaffolds a production-ready service with CI/CD, monitoring, and K8s manifests
spec:
owner: team-platform
type: service
parameters:
- title: Service Details
required:
- name
- owner
- language
properties:
name:
title: Service Name
type: string
pattern: "^[a-z][a-z0-9-]*$"
owner:
title: Owning Team
type: string
ui:field: OwnerPicker
language:
title: Language
type: string
enum:
- typescript
- python
- go
steps:
- id: fetch
name: Fetch Template
action: fetch:template
input:
url: ./skeleton
values:
name: ${{ parameters.name }}
owner: ${{ parameters.owner }}
- id: publish
name: Create Repository
action: publish:github
input:
repoUrl: github.com?owner=myorg&repo=${{ parameters.name }}
defaultBranch: main
- id: register
name: Register in Catalog
action: catalog:register
input:
repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }}
catalogInfoPath: /catalog-info.ymlWhat the developer gets in 30 seconds:
- A new GitHub repo with the codebase scaffolded
- CI/CD pipeline configured and running
- Kubernetes manifests with proper resource limits
- Grafana dashboard provisioned
- PagerDuty service created
- Service registered in the catalog
What it used to take: 2 weeks and 5 Jira tickets across 3 teams.
Observability Integration
Every service in the catalog links directly to its dashboards, logs, and alerts. No more "which Grafana dashboard is for this service?":
// Backstage plugin that embeds service health in the catalog page
export const ServiceHealthCard = () => {
const { entity } = useEntity();
const dashboardUrl = entity.metadata.annotations?.["grafana/dashboard-selector"];
return (
<InfoCard title="Service Health">
<iframe
src={`${GRAFANA_URL}/d/${dashboardUrl}?kiosk`}
width="100%"
height="300"
style={{ border: "none" }}
/>
</InfoCard>
);
};Measuring Adoption
The platform only succeeds if developers use it. Useful adoption metrics to track:
- Catalog coverage — percentage of services registered (target: 100%)
- Template usage — new services created through templates vs manually
- Time to first deploy — how long from "we need a new service" to "it's running in staging"
- Portal active users — weekly active users in Backstage
# Quick catalog coverage check
TOTAL_REPOS=$(gh repo list myorg --json name -q '.[].name' | wc -l)
REGISTERED=$(curl -s "$BACKSTAGE_URL/api/catalog/entities?filter=kind=component" | jq '.length')
echo "Catalog coverage: $REGISTERED / $TOTAL_REPOS"What to Do Differently
- Start smaller — building too many features before validating is a common trap. Start with the catalog and one template.
- Don't migrate everything at once — let teams adopt at their own pace
- Invest in documentation — the platform needs better docs than the tools it replaces
- Assign a dedicated team — a platform that's "maintained by everyone" is maintained by no one
Key Takeaways
- Start with developer pain, not infrastructure — survey your users before building
- The software catalog is the foundation — everything else builds on knowing what exists and who owns it
- Golden paths reduce time-to-production by 10x — self-service scaffolding is the highest-impact feature
- Measure adoption, not features — a platform nobody uses is worse than no platform
- Treat the platform as a product — it needs a roadmap, user research, and a dedicated team