Skip to main content
Back to blogs
Platform Engineering

Platform Engineering: Building an Internal Developer Portal That Gets Used

Most internal platforms fail because they solve infrastructure problems, not developer problems. Here's how to build one that developers actually adopt.

February 10, 20265 min read
platform-engineeringdeveloper-experiencedevopsbackstage

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:

  1. "Nobody knows which service owns this API endpoint" — service discovery
  2. "Setting up a new service takes two weeks of tickets" — scaffolding
  3. "The runbook for this alert is nowhere to be found" — documentation discovery
  4. "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:

catalog-info.yml
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-cache

Every 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:

templates/new-service.yml
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.yml

What 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?":

plugins/observability-card.tsx
// 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

  1. Start smaller — building too many features before validating is a common trap. Start with the catalog and one template.
  2. Don't migrate everything at once — let teams adopt at their own pace
  3. Invest in documentation — the platform needs better docs than the tools it replaces
  4. Assign a dedicated team — a platform that's "maintained by everyone" is maintained by no one

Key Takeaways

  1. Start with developer pain, not infrastructure — survey your users before building
  2. The software catalog is the foundation — everything else builds on knowing what exists and who owns it
  3. Golden paths reduce time-to-production by 10x — self-service scaffolding is the highest-impact feature
  4. Measure adoption, not features — a platform nobody uses is worse than no platform
  5. Treat the platform as a product — it needs a roadmap, user research, and a dedicated team
Share