Skip to content

vanHeemstraSystems/learning-idp-platform-engineering

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

3 Commits
Β 
Β 
Β 
Β 

Repository files navigation

Learning IDP: Platform Engineering

This repository focuses on mastering Platform Engineering concepts and building Internal Development Platforms (IDP) using Backstage.io, service catalogs, and golden paths for developer self-service.

🎯 Learning Objectives

By working through this repository, you will:

  1. Understand Platform Engineering principles and philosophy
  2. Master Backstage.io for building developer portals
  3. Implement service catalogs and software templates
  4. Design golden paths for common workflows
  5. Build developer self-service capabilities
  6. Integrate platform APIs and plugins
  7. Measure platform adoption and developer experience

πŸ“š Prerequisites

πŸ—‚οΈ Directory Structure

learning-idp-platform-engineering/
β”œβ”€β”€ README.md                          # This file
β”œβ”€β”€ REFERENCES.md                      # Links to resources and related repos
β”œβ”€β”€ .gitignore                         # Git ignore patterns
β”œβ”€β”€ .env.example                       # Environment variables template
β”‚
β”œβ”€β”€ docs/
β”‚   β”œβ”€β”€ concepts/
β”‚   β”‚   β”œβ”€β”€ 01-platform-engineering-overview.md
β”‚   β”‚   β”œβ”€β”€ 02-developer-experience.md
β”‚   β”‚   β”œβ”€β”€ 03-golden-paths.md
β”‚   β”‚   β”œβ”€β”€ 04-service-catalog.md
β”‚   β”‚   β”œβ”€β”€ 05-platform-apis.md
β”‚   β”‚   └── 06-platform-metrics.md
β”‚   β”œβ”€β”€ guides/
β”‚   β”‚   β”œβ”€β”€ getting-started-backstage.md
β”‚   β”‚   β”œβ”€β”€ creating-templates.md
β”‚   β”‚   β”œβ”€β”€ plugin-development.md
β”‚   β”‚   β”œβ”€β”€ integrations.md
β”‚   β”‚   └── platform-adoption.md
β”‚   └── examples/
β”‚       β”œβ”€β”€ simple-service-catalog.md
β”‚       β”œβ”€β”€ software-template.md
β”‚       β”œβ”€β”€ custom-plugin.md
β”‚       β”œβ”€β”€ platform-portal.md
β”‚       └── golden-path-workflow.md
β”‚
β”œβ”€β”€ backstage/
β”‚   β”œβ”€β”€ app/
β”‚   β”‚   β”œβ”€β”€ package.json
β”‚   β”‚   β”œβ”€β”€ tsconfig.json
β”‚   β”‚   └── src/
β”‚   β”‚       β”œβ”€β”€ App.tsx
β”‚   β”‚       β”œβ”€β”€ components/
β”‚   β”‚       └── theme/
β”‚   β”‚
β”‚   β”œβ”€β”€ packages/
β”‚   β”‚   β”œβ”€β”€ backend/
β”‚   β”‚   β”‚   β”œβ”€β”€ package.json
β”‚   β”‚   β”‚   └── src/
β”‚   β”‚   β”‚       β”œβ”€β”€ index.ts
β”‚   β”‚   β”‚       └── plugins/
β”‚   β”‚   └── app/
β”‚   β”‚       β”œβ”€β”€ package.json
β”‚   β”‚       └── src/
β”‚   β”‚
β”‚   β”œβ”€β”€ plugins/
β”‚   β”‚   β”œβ”€β”€ azure-devops/
β”‚   β”‚   β”‚   β”œβ”€β”€ package.json
β”‚   β”‚   β”‚   └── src/
β”‚   β”‚   β”œβ”€β”€ cost-insights/
β”‚   β”‚   β”‚   β”œβ”€β”€ package.json
β”‚   β”‚   β”‚   └── src/
β”‚   β”‚   └── platform-metrics/
β”‚   β”‚       β”œβ”€β”€ package.json
β”‚   β”‚       └── src/
β”‚   β”‚
β”‚   β”œβ”€β”€ catalog/
β”‚   β”‚   β”œβ”€β”€ entities/
β”‚   β”‚   β”‚   β”œβ”€β”€ components/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ api-service.yaml
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ web-app.yaml
β”‚   β”‚   β”‚   β”‚   └── microservice.yaml
β”‚   β”‚   β”‚   β”œβ”€β”€ systems/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ platform.yaml
β”‚   β”‚   β”‚   β”‚   └── product.yaml
β”‚   β”‚   β”‚   β”œβ”€β”€ apis/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ rest-api.yaml
β”‚   β”‚   β”‚   β”‚   └── graphql-api.yaml
β”‚   β”‚   β”‚   β”œβ”€β”€ resources/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ database.yaml
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ storage.yaml
β”‚   β”‚   β”‚   β”‚   └── kubernetes.yaml
β”‚   β”‚   β”‚   └── domains/
β”‚   β”‚   β”‚       β”œβ”€β”€ platform.yaml
β”‚   β”‚   β”‚       └── product.yaml
β”‚   β”‚   └── locations/
β”‚   β”‚       └── catalog-info.yaml
β”‚   β”‚
β”‚   β”œβ”€β”€ templates/
β”‚   β”‚   β”œβ”€β”€ python-service/
β”‚   β”‚   β”‚   β”œβ”€β”€ template.yaml
β”‚   β”‚   β”‚   └── skeleton/
β”‚   β”‚   β”‚       β”œβ”€β”€ catalog-info.yaml
β”‚   β”‚   β”‚       β”œβ”€β”€ README.md
β”‚   β”‚   β”‚       └── src/
β”‚   β”‚   β”œβ”€β”€ react-app/
β”‚   β”‚   β”‚   β”œβ”€β”€ template.yaml
β”‚   β”‚   β”‚   └── skeleton/
β”‚   β”‚   β”œβ”€β”€ azure-function/
β”‚   β”‚   β”‚   β”œβ”€β”€ template.yaml
β”‚   β”‚   β”‚   └── skeleton/
β”‚   β”‚   └── infrastructure/
β”‚   β”‚       β”œβ”€β”€ template.yaml
β”‚   β”‚       └── skeleton/
β”‚   β”‚
β”‚   └── techdocs/
β”‚       β”œβ”€β”€ default/
β”‚       β”‚   └── mkdocs.yml
β”‚       └── components/
β”‚
β”œβ”€β”€ platform-apis/
β”‚   β”œβ”€β”€ service-provisioning/
β”‚   β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”‚   β”œβ”€β”€ main.py
β”‚   β”‚   β”‚   β”œβ”€β”€ routers/
β”‚   β”‚   β”‚   └── services/
β”‚   β”‚   └── tests/
β”‚   β”‚
β”‚   β”œβ”€β”€ resource-management/
β”‚   β”‚   β”œβ”€β”€ src/
β”‚   β”‚   └── tests/
β”‚   β”‚
β”‚   └── developer-portal/
β”‚       β”œβ”€β”€ src/
β”‚       └── tests/
β”‚
β”œβ”€β”€ golden-paths/
β”‚   β”œβ”€β”€ web-application/
β”‚   β”‚   β”œβ”€β”€ README.md
β”‚   β”‚   β”œβ”€β”€ template.yaml
β”‚   β”‚   └── workflows/
β”‚   β”‚       β”œβ”€β”€ setup.yaml
β”‚   β”‚       β”œβ”€β”€ deploy.yaml
β”‚   β”‚       └── monitor.yaml
β”‚   β”‚
β”‚   β”œβ”€β”€ api-service/
β”‚   β”‚   β”œβ”€β”€ README.md
β”‚   β”‚   β”œβ”€β”€ template.yaml
β”‚   β”‚   └── workflows/
β”‚   β”‚
β”‚   β”œβ”€β”€ data-pipeline/
β”‚   β”‚   β”œβ”€β”€ README.md
β”‚   β”‚   β”œβ”€β”€ template.yaml
β”‚   β”‚   └── workflows/
β”‚   β”‚
β”‚   └── infrastructure/
β”‚       β”œβ”€β”€ README.md
β”‚       β”œβ”€β”€ template.yaml
β”‚       └── workflows/
β”‚
β”œβ”€β”€ examples/
β”‚   β”œβ”€β”€ 01_basic_catalog/
β”‚   β”‚   β”œβ”€β”€ catalog-info.yaml
β”‚   β”‚   └── README.md
β”‚   β”‚
β”‚   β”œβ”€β”€ 02_software_templates/
β”‚   β”‚   β”œβ”€β”€ python-service-template.yaml
β”‚   β”‚   β”œβ”€β”€ react-app-template.yaml
β”‚   β”‚   └── README.md
β”‚   β”‚
β”‚   β”œβ”€β”€ 03_custom_plugins/
β”‚   β”‚   β”œβ”€β”€ cost-plugin/
β”‚   β”‚   β”œβ”€β”€ metrics-plugin/
β”‚   β”‚   └── README.md
β”‚   β”‚
β”‚   β”œβ”€β”€ 04_techdocs/
β”‚   β”‚   β”œβ”€β”€ component-docs/
β”‚   β”‚   └── README.md
β”‚   β”‚
β”‚   └── 05_integrations/
β”‚       β”œβ”€β”€ azure-devops/
β”‚       β”œβ”€β”€ github-actions/
β”‚       └── README.md
β”‚
β”œβ”€β”€ automation/
β”‚   β”œβ”€β”€ onboarding/
β”‚   β”‚   β”œβ”€β”€ create-project.py
β”‚   β”‚   β”œβ”€β”€ setup-permissions.py
β”‚   β”‚   └── configure-resources.py
β”‚   β”‚
β”‚   β”œβ”€β”€ scaffolding/
β”‚   β”‚   β”œβ”€β”€ generate-service.py
β”‚   β”‚   └── generate-infrastructure.py
β”‚   β”‚
β”‚   └── monitoring/
β”‚       β”œβ”€β”€ track-adoption.py
β”‚       └── measure-experience.py
β”‚
β”œβ”€β”€ metrics/
β”‚   β”œβ”€β”€ dashboards/
β”‚   β”‚   β”œβ”€β”€ platform-adoption.json
β”‚   β”‚   β”œβ”€β”€ developer-experience.json
β”‚   β”‚   └── service-catalog.json
β”‚   β”‚
β”‚   └── queries/
β”‚       β”œβ”€β”€ usage-metrics.kql
β”‚       └── experience-metrics.kql
β”‚
β”œβ”€β”€ infrastructure/
β”‚   β”œβ”€β”€ backstage-deployment/
β”‚   β”‚   β”œβ”€β”€ kubernetes/
β”‚   β”‚   β”‚   β”œβ”€β”€ deployment.yaml
β”‚   β”‚   β”‚   β”œβ”€β”€ service.yaml
β”‚   β”‚   β”‚   └── ingress.yaml
β”‚   β”‚   └── bicep/
β”‚   β”‚       └── backstage.bicep
β”‚   β”‚
β”‚   └── platform-services/
β”‚       β”œβ”€β”€ api-gateway/
β”‚       └── service-mesh/
β”‚
β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ unit/
β”‚   β”‚   β”œβ”€β”€ test_templates.py
β”‚   β”‚   └── test_plugins.py
β”‚   β”‚
β”‚   β”œβ”€β”€ integration/
β”‚   β”‚   β”œβ”€β”€ test_catalog.py
β”‚   β”‚   └── test_workflows.py
β”‚   β”‚
β”‚   └── e2e/
β”‚       └── test_developer_flow.py
β”‚
└── .github/
    └── workflows/
        β”œβ”€β”€ backstage-deploy.yml
        β”œβ”€β”€ template-validation.yml
        └── plugin-tests.yml

πŸš€ Getting Started

1. Clone the Repository

git clone https://github.com/vanHeemstraSystems/learning-idp-platform-engineering.git
cd learning-idp-platform-engineering

2. Install Prerequisites

# Install Node.js 18+ and Yarn
# On Ubuntu/Debian:
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs
npm install -g yarn

# Verify installations
node --version
yarn --version

3. Create Your First Backstage App

# Create Backstage app
npx @backstage/create-app@latest

# Navigate to app directory
cd my-backstage-app

# Start development server
yarn dev

4. Add Your First Service to Catalog

# catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: my-first-service
  description: My first service in the platform
  annotations:
    github.com/project-slug: my-org/my-first-service
spec:
  type: service
  lifecycle: production
  owner: team-platform
  system: platform

πŸ“– Learning Path

Follow this recommended sequence:

Week 1: Platform Engineering Fundamentals

Day 1-2: Platform Engineering Concepts

  1. Read docs/concepts/01-platform-engineering-overview.md
  2. Study docs/concepts/02-developer-experience.md
  3. Understand platform vs product thinking

Day 3-4: Backstage.io Setup

  1. Follow docs/guides/getting-started-backstage.md
  2. Set up local Backstage instance
  3. Explore default plugins

Day 5-7: Service Catalog

  1. Read docs/concepts/04-service-catalog.md
  2. Add services to catalog
  3. Define ownership and relationships

Week 2: Software Templates & Golden Paths

Day 1-3: Software Templates

  1. Study docs/guides/creating-templates.md
  2. Work through examples/02_software_templates/
  3. Create custom templates

Day 4-7: Golden Paths

  1. Read docs/concepts/03-golden-paths.md
  2. Design workflow golden paths
  3. Implement in golden-paths/

Week 3: Plugins & Integrations

Day 1-4: Plugin Development

  1. Study docs/guides/plugin-development.md
  2. Work through examples/03_custom_plugins/
  3. Create custom plugin

Day 5-7: Integrations

  1. Complete docs/guides/integrations.md
  2. Integrate Azure DevOps
  3. Configure GitHub Actions

Week 4: Platform APIs & Metrics

Day 1-3: Platform APIs

  1. Read docs/concepts/05-platform-apis.md
  2. Build self-service APIs
  3. Implement in platform-apis/

Day 4-7: Metrics & Adoption

  1. Study docs/concepts/06-platform-metrics.md
  2. Implement platform metrics
  3. Track developer experience

πŸ”‘ Key Concepts

Platform Engineering Principles

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚     PLATFORM ENGINEERING GOALS          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1. Reduce cognitive load               β”‚
β”‚ 2. Enable self-service                 β”‚
β”‚ 3. Standardize workflows               β”‚
β”‚ 4. Improve developer experience        β”‚
β”‚ 5. Increase development velocity       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Golden Path Architecture

Developer Intent
      ↓
  Golden Path
      ↓
   Template
      ↓
  Automation
      ↓
   Resources
      ↓
  Monitoring

πŸ’‘ Common Implementations

Service Catalog Entry

# catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: payment-service
  description: Payment processing microservice
  annotations:
    github.com/project-slug: my-org/payment-service
    azure-devops.com/project-repo: my-org/payment-service
  tags:
    - python
    - fastapi
    - payments
  links:
    - url: https://payment-api.example.com
      title: Production API
      icon: dashboard
    - url: https://docs.example.com/payment-service
      title: Documentation
      icon: docs
spec:
  type: service
  lifecycle: production
  owner: team-payments
  system: payment-platform
  dependsOn:
    - resource:payment-database
    - component:notification-service
  providesApis:
    - payment-api

Software Template

# templates/python-service/template.yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: python-fastapi-service
  title: Python FastAPI Service
  description: Create a new Python FastAPI microservice
  tags:
    - python
    - fastapi
    - recommended
spec:
  owner: team-platform
  type: service
  
  parameters:
    - title: Service Information
      required:
        - name
        - description
        - owner
      properties:
        name:
          title: Service Name
          type: string
          description: Unique name for your service
          ui:autofocus: true
        description:
          title: Description
          type: string
          description: What does this service do?
        owner:
          title: Owner
          type: string
          description: Team owning this service
          ui:field: OwnerPicker
          ui:options:
            catalogFilter:
              kind: Group
    
    - title: Azure Configuration
      required:
        - resourceGroup
        - location
      properties:
        resourceGroup:
          title: Resource Group
          type: string
          description: Azure Resource Group name
        location:
          title: Location
          type: string
          description: Azure region
          enum:
            - westeurope
            - northeurope
            - eastus
          default: westeurope
  
  steps:
    - id: fetch-base
      name: Fetch Base Template
      action: fetch:template
      input:
        url: ./skeleton
        values:
          name: ${{ parameters.name }}
          description: ${{ parameters.description }}
          owner: ${{ parameters.owner }}
    
    - id: publish
      name: Publish to GitHub
      action: publish:github
      input:
        allowedHosts: ['github.com']
        description: ${{ parameters.description }}
        repoUrl: github.com?owner=my-org&repo=${{ parameters.name }}
    
    - id: register
      name: Register Component
      action: catalog:register
      input:
        repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }}
        catalogInfoPath: '/catalog-info.yaml'
    
    - id: create-azure-resources
      name: Create Azure Resources
      action: azure:resource-group:create
      input:
        name: ${{ parameters.resourceGroup }}
        location: ${{ parameters.location }}
  
  output:
    links:
      - title: Repository
        url: ${{ steps.publish.output.remoteUrl }}
      - title: Open in Backstage
        icon: catalog
        entityRef: ${{ steps.register.output.entityRef }}

Custom Plugin Structure

// plugins/platform-metrics/src/plugin.ts
import { 
  createPlugin,
  createRoutableExtension,
} from '@backstage/core-plugin-api';

export const platformMetricsPlugin = createPlugin({
  id: 'platform-metrics',
  routes: {
    root: rootRouteRef,
  },
});

export const PlatformMetricsPage = platformMetricsPlugin.provide(
  createRoutableExtension({
    name: 'PlatformMetricsPage',
    component: () =>
      import('./components/PlatformMetricsPage').then(
        m => m.PlatformMetricsPage
      ),
    mountPoint: rootRouteRef,
  })
);
// plugins/platform-metrics/src/components/PlatformMetricsPage.tsx
import React from 'react';
import { Grid, Card, CardContent, Typography } from '@material-ui/core';
import { InfoCard } from '@backstage/core-components';

export const PlatformMetricsPage = () => {
  const metrics = usePlatformMetrics();
  
  return (
    <Grid container spacing={3}>
      <Grid item xs={12} md={4}>
        <InfoCard title="Active Services">
          <Typography variant="h3">
            {metrics.activeServices}
          </Typography>
        </InfoCard>
      </Grid>
      
      <Grid item xs={12} md={4}>
        <InfoCard title="Deployments This Week">
          <Typography variant="h3">
            {metrics.weeklyDeployments}
          </Typography>
        </InfoCard>
      </Grid>
      
      <Grid item xs={12} md={4}>
        <InfoCard title="Developer Satisfaction">
          <Typography variant="h3">
            {metrics.satisfaction}%
          </Typography>
        </InfoCard>
      </Grid>
    </Grid>
  );
};

Platform API Example

# platform-apis/service-provisioning/src/main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional

app = FastAPI(
    title="Platform Service Provisioning API",
    description="Self-service API for provisioning platform services"
)

class ServiceRequest(BaseModel):
    name: str
    type: str
    owner: str
    environment: str
    config: dict

class ServiceResponse(BaseModel):
    id: str
    name: str
    status: str
    endpoints: List[str]
    repository_url: str

@app.post("/services", response_model=ServiceResponse)
async def create_service(request: ServiceRequest):
    """
    Create a new service using the platform's golden path
    """
    # Validate request
    if request.type not in ["api", "web", "worker"]:
        raise HTTPException(status_code=400, detail="Invalid service type")
    
    # Create from template
    service = await create_from_template(
        name=request.name,
        type=request.type,
        owner=request.owner
    )
    
    # Provision infrastructure
    infrastructure = await provision_infrastructure(
        service_id=service.id,
        environment=request.environment,
        config=request.config
    )
    
    # Register in service catalog
    await register_in_catalog(service)
    
    return ServiceResponse(
        id=service.id,
        name=service.name,
        status="provisioning",
        endpoints=infrastructure.endpoints,
        repository_url=service.repository_url
    )

@app.get("/services/{service_id}", response_model=ServiceResponse)
async def get_service(service_id: str):
    """
    Get service details
    """
    service = await fetch_service(service_id)
    if not service:
        raise HTTPException(status_code=404, detail="Service not found")
    return service

🎯 Best Practices

1. Design for Self-Service

# Enable developers to provision their own resources
template:
  parameters:
    - user inputs (minimal)
  steps:
    - validate
    - provision
    - configure
    - register
    - notify

2. Implement Golden Paths

Golden Path = Template + Automation + Best Practices

Components:
- Pre-configured templates
- Automated workflows
- Built-in security
- Observability by default
- Documentation included

3. Measure Everything

# Platform metrics to track
metrics = {
    "adoption": {
        "active_services": count,
        "template_usage": count,
        "developer_signups": count
    },
    "experience": {
        "time_to_first_deploy": duration,
        "template_completion_rate": percentage,
        "support_tickets": count
    },
    "performance": {
        "provisioning_time": duration,
        "deployment_frequency": rate,
        "success_rate": percentage
    }
}

4. Iterate Based on Feedback

Feedback Loop:
1. Gather developer feedback
2. Analyze pain points
3. Improve platform
4. Measure impact
5. Repeat

πŸ”— Related Repositories

🀝 Contributing

This is a personal learning repository, but suggestions and improvements are welcome!

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes with tests
  4. Ensure all tests pass
  5. Submit a pull request

πŸ“„ License

This project is for educational purposes. See LICENSE file for details.

πŸ“§ Contact

Willem van Heemstra


Last updated: December 18, 2025 Part of the learning-internal-development-platform series

About

Learning IDP Platform Engineering

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published