Skip to content

Platform Engineering: Developer Platforms That Scale

Design internal developer platforms that abstract infrastructure, offer self-service, cut cognitive load on application teams and speed up delivery.

4 min read
Internal developer platform architecture showing self-service portal, platform APIs, and infrastructure abstractions

DevOps promised "you build it, you run it." In practice, this means every application team needs expertise in Kubernetes, Terraform, CI/CD pipelines, observability, and security scanning. Platform engineering provides a different answer: build a curated, self-service platform that handles infrastructure complexity so application teams can focus on delivering business value.

The key distinction is that platform teams build products for developers, not tickets for developers.

The Platform as a Product

Treating the platform as a product means understanding your users (developers), their jobs to be done, and measuring whether the platform actually improves their workflow.

tstypescript
interface PlatformCapability {
  name: string;
  selfService: boolean;
  currentState: "manual" | "semi-automated" | "fully-automated";
  developerFriction: "high" | "medium" | "low";
  usage: number; // requests per week
}
 
const platformCapabilities: PlatformCapability[] = [
  {
    name: "Create new service",
    selfService: false,
    currentState: "manual",
    developerFriction: "high",
    usage: 5,
  },
  {
    name: "Deploy to production",
    selfService: true,
    currentState: "fully-automated",
    developerFriction: "low",
    usage: 200,
  },
  {
    name: "Provision database",
    selfService: false,
    currentState: "manual",
    developerFriction: "high",
    usage: 8,
  },
  {
    name: "Add monitoring dashboard",
    selfService: false,
    currentState: "semi-automated",
    developerFriction: "medium",
    usage: 15,
  },
  {
    name: "Rotate secrets",
    selfService: true,
    currentState: "fully-automated",
    developerFriction: "low",
    usage: 30,
  },
];
 
// Prioritize by friction × frequency
function prioritizeCapabilities(
  capabilities: PlatformCapability[]
): PlatformCapability[] {
  const frictionScore = { high: 3, medium: 2, low: 1 };
 
  return [...capabilities].sort((a, b) => {
    const scoreA = frictionScore[a.developerFriction] * a.usage;
    const scoreB = frictionScore[b.developerFriction] * b.usage;
    return scoreB - scoreA;
  });
}

Service Templates and Golden Paths

The most impactful platform feature is service scaffolding. Instead of copying and pasting from existing services and fixing configuration for days, developers get a production-ready service in minutes.

tstypescript
// ❌ The copy-paste-and-pray approach
// 1. Clone existing service repo
// 2. Rename everything (miss some references)
// 3. Update CI/CD config (copy wrong secrets)
// 4. Modify Kubernetes manifests (break resource limits)
// 5. Change database config (point to wrong cluster)
// 6. Spend 3 days debugging why deployment fails
tstypescript
// ✅ Platform-provided service templates
interface ServiceTemplate {
  name: string;
  language: string;
  includes: string[];
  parameters: TemplateParameter[];
}
 
interface TemplateParameter {
  name: string;
  description: string;
  type: "string" | "select" | "boolean";
  options?: string[];
  defaultValue?: string;
  required: boolean;
}
 
const templates: ServiceTemplate[] = [
  {
    name: "Node.js API Service",
    language: "typescript",
    includes: [
      "Express server with health checks",
      "OpenTelemetry instrumentation",
      "Structured logging (pino)",
      "Dockerfile with multi-stage build",
      "Kubernetes manifests (deployment, service, HPA)",
      "CI/CD pipeline (build, test, deploy)",
      "Grafana dashboard template",
      "Alert rules for SLOs",
      "README with runbook links",
    ],
    parameters: [
      {
        name: "serviceName",
        description: "Name of the service (lowercase, hyphens)",
        type: "string",
        required: true,
      },
      {
        name: "team",
        description: "Owning team",
        type: "select",
        options: ["payments", "catalog", "identity", "notifications"],
        required: true,
      },
      {
        name: "database",
        description: "Database type",
        type: "select",
        options: ["postgresql", "none"],
        defaultValue: "postgresql",
        required: false,
      },
      {
        name: "messageQueue",
        description: "Include message queue consumer",
        type: "boolean",
        defaultValue: "false",
        required: false,
      },
    ],
  },
];

The golden path isn't mandatory—it's the easiest option. Teams can diverge if they have good reasons, but the default path is so well-paved that most teams prefer it.

The Platform API Layer

A platform API provides programmatic access to infrastructure operations. This enables both the self-service portal and automated workflows.

tstypescript
interface PlatformAPI {
  services: {
    create(config: ServiceConfig): Promise<ServiceInstance>;
    deploy(serviceId: string, version: string): Promise<Deployment>;
    scale(serviceId: string, replicas: number): Promise<void>;
    getStatus(serviceId: string): Promise<ServiceStatus>;
  };
  databases: {
    provision(config: DatabaseConfig): Promise<DatabaseInstance>;
    createBackup(dbId: string): Promise<BackupInfo>;
    restoreBackup(dbId: string, backupId: string): Promise<void>;
  };
  secrets: {
    set(key: string, value: string, scope: string): Promise<void>;
    rotate(key: string): Promise<void>;
    listScopes(): Promise<string[]>;
  };
  observability: {
    createDashboard(config: DashboardConfig): Promise<string>;
    createAlert(config: AlertConfig): Promise<string>;
  };
}
 
interface ServiceConfig {
  name: string;
  team: string;
  template: string;
  environment: "dev" | "staging" | "production";
  resources: {
    cpu: string;
    memory: string;
    replicas: number;
  };
}
 
interface ServiceStatus {
  name: string;
  environment: string;
  currentVersion: string;
  replicas: { desired: number; ready: number };
  health: "healthy" | "degraded" | "unhealthy";
  lastDeployment: {
    version: string;
    timestamp: Date;
    status: "success" | "failed" | "rolling-back";
  };
}

Developer Experience Metrics

Platform success is measured by developer productivity, not infrastructure metrics. Track how long common workflows take and where developers get stuck.

tstypescript
interface DeveloperExperienceMetrics {
  timeToFirstDeploy: number;        // minutes: new service → production
  deploymentFrequency: number;      // deploys per team per week
  leadTimeForChanges: number;       // minutes: commit → production
  ticketsToInfraTeam: number;       // per team per month
  platformAdoptionRate: number;     // % of services using golden paths
  developerSatisfaction: number;    // quarterly survey, 1-5
}
 
function assessPlatformMaturity(
  metrics: DeveloperExperienceMetrics
): { level: string; nextSteps: string[] } {
  const nextSteps: string[] = [];
 
  if (metrics.timeToFirstDeploy > 480) {
    nextSteps.push(
      `Time to first deploy is ${metrics.timeToFirstDeploy} min. ` +
      "Target: under 60 min via service templates."
    );
  }
 
  if (metrics.ticketsToInfraTeam > 10) {
    nextSteps.push(
      `${metrics.ticketsToInfraTeam} infra tickets/month per team. ` +
      "Identify top ticket categories and build self-service for them."
    );
  }
 
  if (metrics.platformAdoptionRate < 0.7) {
    nextSteps.push(
      `Only ${(metrics.platformAdoptionRate * 100).toFixed(0)}% adoption. ` +
      "Interview non-adopters to understand gaps."
    );
  }
 
  if (metrics.developerSatisfaction < 3.5) {
    nextSteps.push(
      `Satisfaction at ${metrics.developerSatisfaction}/5. ` +
      "Run developer experience interviews for qualitative feedback."
    );
  }
 
  const level =
    nextSteps.length === 0
      ? "mature"
      : nextSteps.length <= 2
        ? "growing"
        : "foundational";
 
  return { level, nextSteps };
}

Avoiding Platform Team Anti-Patterns

Platform teams fail when they build an internal cloud provider instead of an opinionated workflow.

tstypescript
interface PlatformAntiPattern {
  pattern: string;
  symptom: string;
  fix: string;
}
 
const antiPatterns: PlatformAntiPattern[] = [
  {
    pattern: "Building AWS-on-AWS",
    symptom: "Platform exposes raw Terraform/Kubernetes primitives to developers",
    fix: "Abstract to workflow-level operations: 'deploy my service' not 'create an ingress'",
  },
  {
    pattern: "Ticket-driven platform",
    symptom: "Developers file tickets and wait for platform team to act",
    fix: "Build self-service APIs and UIs; platform team builds tools, not fulfills requests",
  },
  {
    pattern: "Mandatory adoption without value",
    symptom: "Teams forced to use platform tools that make their workflow worse",
    fix: "Make the platform the easiest path, not the required path",
  },
  {
    pattern: "Building without user research",
    symptom: "Platform features nobody asked for, missing features everyone needs",
    fix: "Embed with application teams; watch them work; fix their actual pain points",
  },
  {
    pattern: "No documentation or onboarding",
    symptom: "Only platform team members can use the platform effectively",
    fix: "Write getting-started guides, record walkthroughs, staff office hours",
  },
];

Key Takeaways

Platform engineering succeeds when you treat the platform as a product with developer teams as your users. Start by measuring where developers spend time on toil—provisioning services, debugging deployment pipelines, wiring observability—and build self-service automation for the highest-friction, highest-frequency workflows first. Provide golden path templates that give teams a production-ready starting point with observability, CI/CD, and security built in. Expose a platform API that powers both self-service UIs and automated workflows. Measure success through developer experience metrics: time to first deploy, infra ticket volume, and developer satisfaction. The platform team that ships the fewest features but eliminates the most developer toil is the one that succeeds.

Wilfredo Rujel

Wilfredo Rujel

Full Stack Software Engineer

Share this postX