Platform Engineering in 2026: Building Internal Developer Platforms with Backstage
on Platform engineering, Backstage, Devops, Internal developer platform, Developer experience, Kubernetes, Devex
Platform Engineering in 2026: Building Internal Developer Platforms with Backstage
The DevOps movement told every developer to own their infrastructure. The result, a decade later: developers drowning in Kubernetes YAML, Terraform modules, security policies, observability configs, and deployment pipelines. The cognitive load became unsustainable.
Platform Engineering is the response. Instead of “you build it, you run it” meaning “you configure everything yourself,” platform engineering creates golden paths — opinionated, self-service workflows that let developers deploy with confidence without needing to become infrastructure experts.
At the center of most modern IDPs sits Backstage — Spotify’s open-source developer portal, now a CNCF project used by Airbnb, American Airlines, Spotify, and hundreds more.
What Is an Internal Developer Platform?
An IDP is not a CI/CD tool. It’s not a Kubernetes dashboard. It’s an abstraction layer over your entire engineering platform that surfaces the right capabilities at the right level of abstraction for your developers.
A mature IDP provides:
- Software catalog — Every service, library, API, team, and resource in one place
- Self-service scaffolding — Create a new production-ready service in minutes
- Environment management — Spin up dev/staging environments without ops tickets
- Deployment pipelines — One-click deploys with built-in best practices
- Observability — Dashboards, traces, and alerts pre-wired for every service
- Documentation — Auto-generated and always up to date
The North Star: a developer should be able to go from idea to production without filing a ticket or bothering the platform team.
Backstage: The Building Blocks
Software Catalog
The catalog is Backstage’s foundation. It aggregates metadata from all your systems into a unified graph:
# catalog-info.yaml (lives in every service repo)
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: payments-service
description: Handles all payment processing and billing
annotations:
github.com/project-slug: myorg/payments-service
grafana/dashboard-selector: "service=payments"
pagerduty.com/service-id: P1234567
sonarqube.org/project-key: payments-service
backstage.io/techdocs-ref: dir:.
tags:
- golang
- payments
- critical
links:
- url: https://grafana.internal/d/payments
title: Production Dashboard
- url: https://payments.internal/docs/api
title: API Documentation
spec:
type: service
lifecycle: production
owner: team:payments
system: billing-platform
dependsOn:
- component:postgres-payments
- component:stripe-integration
- resource:payments-kafka-topic
providesApis:
- payments-api
Backstage discovers these files via GitHub, GitLab, or Bitbucket integrations and builds a live dependency graph of your entire organization’s software.
Software Templates: The Golden Path
Templates are where platform engineering delivers its biggest ROI. A well-designed template gives a developer a production-ready service with:
- Proper project structure
- Dockerized build
- CI/CD pipeline
- Kubernetes manifests
- Monitoring dashboards
- Alert rules
- Auto-registered in the catalog
All from a single form fill.
# templates/golang-service/template.yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: golang-microservice
title: Go Microservice
description: Production-ready Go service with Kubernetes, monitoring, and CI/CD
tags:
- golang
- 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, hyphenated (e.g., user-service)
description:
title: Description
type: string
owner:
title: Owner Team
type: string
ui:field: OwnerPicker
ui:options:
allowedKinds: [Group]
- title: Infrastructure
properties:
database:
title: Database
type: string
enum: [none, postgresql, mysql, mongodb]
default: none
cache:
title: Cache
type: string
enum: [none, redis]
default: none
initialReplicas:
title: Initial Replica Count
type: integer
default: 2
minimum: 1
maximum: 10
steps:
- id: fetch-template
name: Fetch Template
action: fetch:template
input:
url: ./skeleton
values:
name: $
description: $
owner: $
database: $
cache: $
- id: create-repo
name: Create GitHub Repository
action: github:repo:create
input:
repoUrl: github.com?owner=myorg&repo=$
description: $
defaultBranch: main
topics:
- go
- microservice
- id: push-code
name: Push Initial Code
action: github:repo:push
input:
repoUrl: github.com?owner=myorg&repo=$
defaultBranch: main
- id: create-pagerduty-service
name: Create PagerDuty Service
action: pagerduty:service:create
input:
name: $
escalationPolicyId: P_PLATFORM_DEFAULT
- id: register-catalog
name: Register in Catalog
action: catalog:register
input:
repoContentsUrl: $
catalogInfoPath: /catalog-info.yaml
output:
links:
- title: Repository
url: $
- title: Open in Catalog
entityRef: $
Photo by Marvin Meyer on Unsplash
Custom Plugins: Extending Backstage
Backstage’s plugin system is where it gets powerful. Every company’s platform is different — custom plugins let you surface the exact right information for your developers.
Building a Custom Plugin
// plugins/my-deployments/src/components/DeploymentList.tsx
import React, { useEffect, useState } from 'react';
import { useEntity } from '@backstage/plugin-catalog-react';
import { useApi } from '@backstage/core-plugin-api';
import { myDeploymentsApiRef } from '../api';
export const DeploymentList = () => {
const { entity } = useEntity();
const deploymentsApi = useApi(myDeploymentsApiRef);
const [deployments, setDeployments] = useState([]);
useEffect(() => {
const serviceName = entity.metadata.name;
deploymentsApi.getDeployments(serviceName).then(setDeployments);
}, [entity.metadata.name, deploymentsApi]);
return (
<InfoCard title="Recent Deployments">
<Table
columns={[
{ title: 'Version', field: 'version' },
{ title: 'Environment', field: 'environment' },
{ title: 'Deployed By', field: 'deployedBy' },
{ title: 'Status', field: 'status', render: row => (
<StatusBadge status={row.status} />
)},
{ title: 'Time', field: 'deployedAt', render: row => (
<RelativeTime timestamp={row.deployedAt} />
)},
]}
data={deployments}
options=
/>
</InfoCard>
);
};
// Register the plugin in your Backstage app
// packages/app/src/components/catalog/EntityPage.tsx
import { DeploymentList } from '@mycompany/backstage-plugin-deployments';
const serviceEntityPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
<EntityAboutCard />
</EntityLayout.Route>
<EntityLayout.Route path="/deployments" title="Deployments">
<DeploymentList />
</EntityLayout.Route>
<EntityLayout.Route path="/kubernetes" title="Kubernetes">
<EntityKubernetesContent />
</EntityLayout.Route>
<EntityLayout.Route path="/ci-cd" title="CI/CD">
<EntityGithubActionsContent />
</EntityLayout.Route>
<EntityLayout.Route path="/monitoring" title="Monitoring">
<EntityGrafanaDashboardsCard />
</EntityLayout.Route>
</EntityLayout>
);
Environment Management
One of the highest-value IDP capabilities: self-service ephemeral environments.
// Backstage action for creating preview environments
createTemplateAction({
id: 'mycompany:environment:create',
schema: {
input: z.object({
serviceName: z.string(),
prNumber: z.number(),
ttlHours: z.number().default(48),
}),
},
async handler(ctx) {
const { serviceName, prNumber, ttlHours } = ctx.input;
const envName = `pr-${prNumber}-${serviceName}`;
// Create namespace in Kubernetes
await k8sApi.createNamespace({
metadata: {
name: envName,
labels: {
'environment-type': 'ephemeral',
'pr-number': String(prNumber),
'service': serviceName,
'ttl-hours': String(ttlHours),
},
},
});
// Deploy service with Helm
await helmClient.upgrade(envName, `./charts/${serviceName}`, {
namespace: envName,
values: {
image: { tag: `pr-${prNumber}` },
ingress: { host: `${envName}.preview.example.com` },
replicas: 1, // reduced for preview
},
});
ctx.output('previewUrl', `https://${envName}.preview.example.com`);
ctx.output('namespaceName', envName);
},
});
Measuring Platform Success
Platform teams often struggle to demonstrate value. Track these metrics:
DORA Metrics via Catalog
// plugins/dora-metrics/src/api.ts
export async function getDoraMetrics(serviceName: string, days: number = 30) {
const [deployments, incidents, prs] = await Promise.all([
fetchDeployments(serviceName, days),
fetchIncidents(serviceName, days),
fetchMergedPRs(serviceName, days),
]);
const deploymentFrequency = deployments.length / days;
const changeLeadTime = median(prs.map(pr => pr.mergeTime - pr.firstCommitTime));
const changeFailureRate = incidents.filter(i => i.causedByDeploy).length / deployments.length;
const mttr = median(incidents.map(i => i.resolvedAt - i.createdAt));
return { deploymentFrequency, changeLeadTime, changeFailureRate, mttr };
}
Developer Satisfaction
Run automated pulse surveys through Backstage. Track:
- Time to first production deployment (new hire onboarding)
- Self-service rate (% of tasks completed without platform team help)
- Platform NPS score
Real-World ROI
Data from teams that invested in IDPs (2025 DORA Report):
- 45% faster onboarding — New engineers deploy to production in days, not weeks
- 60% reduction in ops tickets — Developers self-serve instead of waiting
- 3x deployment frequency — Golden paths make deploying safe and easy
- 35% fewer production incidents — Platform-enforced standards catch problems early
Getting Started: The Platform Team’s Roadmap
Month 1-2: Foundation
- Deploy Backstage with GitHub/GitLab integration
- Import your existing services into the catalog
- Add TechDocs for documentation
Month 3-4: Golden Paths
- Build 2-3 software templates for your most common service types
- Integrate CI/CD pipeline visibility
- Add Kubernetes status to entity pages
Month 5-6: Self-Service
- Environment provisioning via templates
- Database provisioning automation
- Secrets management integration
Month 7+: Advanced
- Custom plugins for internal systems
- DORA metrics dashboard
- Developer productivity analytics
Conclusion
Platform Engineering is the acknowledgment that “full stack developer who also manages infrastructure” doesn’t scale. Organizations that invest in IDPs — and Backstage is the best open-source option in 2026 — consistently out-ship and out-recruit competitors.
The key insight: developers shouldn’t need to understand Kubernetes to deploy to Kubernetes. The platform team’s job is to make the right thing the easy thing.
Start small. Build one template that works really well. Get developer feedback. Iterate. The platform will pay for itself many times over.
Resources:
- Backstage.io
- Backstage Plugin Marketplace
- CNCF Platform Engineering Whitepaper
- DORA State of DevOps Report 2025
- Team Topologies (Book)
이 글이 도움이 되셨다면 공감 및 광고 클릭을 부탁드립니다 :)
