🚀 Platform Engineer Lektion 18/50 ~9 Min. Experte

Platform API Design und Developer Contracts

APIs, CRDs und Contracts zwischen Platform- und Stream-Teams.

📝 Meine Notizen

Lernziele

Nach dieser Lektion kannst du erklären, warum eine Plattform ein definiertes API-Surface braucht statt einer Sammlung von Tools, und welche Formen dieses Surface annehmen kann: Kubernetes-CRDs, REST-APIs, Git-Konventionen und CLI. Du verstehst, wie man Abstraktionsebenen wählt, was ein Developer Contract über die technische Schnittstelle hinaus regelt, und wie API-Versionierung und Deprecation funktionieren, wenn die Konsumenten interne Teams sind.

Das Problem: Die Plattform hat hundert Türen

Eine gewachsene Plattform bietet Teams viele Eingänge gleichzeitig: ein bisschen Terraform hier, ein Helm-Chart da, drei Wiki-Seiten mit „so machen wir das”, ein Slack-Channel für den Rest. Jeder Eingang hat eigene Semantik, eigene Fehlermodi, eigenen Dokumentationsstand. Für die Konsumenten heißt das: Die Plattform ist nicht eine Sache, die man lernen kann, sondern ein Gelände, das man erkunden muss — der Cognitive Load, den die Plattform reduzieren sollte, kommt durch die Hintertür zurück.

Schlimmer noch für das Platform-Team selbst: Ohne definiertes Interface gibt es keine Grenze zwischen „öffentlich zugesichert” und „internes Implementierungsdetail”. Jede Änderung kann irgendeinen Konsumenten brechen, also traut sich niemand mehr zu ändern. Das ist exakt das Problem, das öffentliche APIs seit Jahrzehnten mit expliziten Verträgen lösen — und eine interne Plattform ist nichts anderes als ein Produkt mit einer API, deren Kunden zufällig im selben Unternehmen sitzen.

Das API-Surface: vier Formen, ein Vertrag

Eine Plattform-API muss kein REST-Endpoint sein. In der Praxis besteht das Surface aus mehreren Formen, die dieselben Abstraktionen anbieten sollten:

Kubernetes CRDs sind das stärkste Fundament, wenn Kubernetes deine Plattform-Basis ist: Eine PostgreSQLInstance oder ein AcmeService als Custom Resource bekommt gratis, was eine API braucht — Schema-Validierung (OpenAPI v3 in der CRD), Versionierung (v1alpha1v1beta1v1), RBAC, Audit-Log und einen Reconciliation-Loop dahinter (Crossplane aus Lektion 16 oder eigene Controller). Deklarativ heißt außerdem: Das Interface beschreibt Zielzustand, nicht Arbeitsschritte — robuster gegen Teilausfälle als jede imperative API.

Git-Konventionen — die Struktur von Repos, die values.yaml des Plattform-Charts, die Promotion-Verzeichnisse — sind ebenfalls API, auch wenn sie selten so behandelt werden. Wenn die Plattform erwartet, dass deploy/prod/ existiert, ist das ein Vertragsbestandteil und gehört dokumentiert und versioniert.

REST/gRPC-APIs braucht es für alles Synchron-Abfragende, das nicht gut in Reconciliation passt: „Welche Umgebungen hat mein Service?”, Kosten-Abfragen, Portal-Backends.

CLI und Portal sind dünne Clients über den anderen Formen — wichtig: über, nicht neben. Wenn das Portal Dinge kann, die per API nicht gehen, hast du zwei APIs mit unterschiedlichem Funktionsumfang und doppelter Wartung.

Die Designregel über alle Formen: Ein Konzept, eine Repräsentation. „Service-Größe” sollte im CRD, im Portal-Formular und in der CLI dasselbe Enum mit derselben Bedeutung sein.

Die Abstraktionsentscheidung: Was versprichst du — und was versteckst du?

Die schwerste Designfrage ist nicht technisch, sondern semantisch: Auf welcher Höhe abstrahierst du? Ein Beispiel mit drei Stufen für „Team braucht eine Datenbank”:

  1. Durchgereicht: Das Team konfiguriert RDS-Parameter direkt (Instance-Typ, IOPS, Parameter Groups). Maximale Flexibilität, null Cognitive-Load-Ersparnis, und die Plattform kann den Provider nie wechseln.
  2. Abstrahiert: size: small | medium | large, backup: standard | extended. Die Plattform mappt auf konkrete Ressourcen und kann das Mapping ändern, ohne den Vertrag zu brechen.
  3. Intent-basiert: „Ich brauche relationale Persistenz mit <5 ms Latenz im selben Cluster.” Die Plattform wählt sogar die Technologie.

Stufe 2 ist fast immer der richtige Default: hoch genug, dass die Plattform Implementierungsfreiheit behält, konkret genug, dass Teams verstehen, was sie bekommen. Entscheidend ist der Escape Hatch: ein dokumentierter, bewusst unbequemerer Weg für die 5 % der Fälle, die die Abstraktion nicht abdeckt — etwa ein parametersOverride-Feld, dessen Nutzung Support-Garantien einschränkt und im Review auffällt. Ohne Escape Hatch erzwingt der erste Sonderfall einen Plattform-Umbau oder treibt das Team in die Schatten-IT.

Der Developer Contract umfasst dabei mehr als das Schema. Er beantwortet schriftlich: Was garantiert die Plattform (Verfügbarkeit der API, Provisioning-Dauer, Backup-Verhalten)? Was wird vom Team erwartet (Labels, Ownership-Angaben, Ressourcen-Limits)? Was ist explizit nicht zugesichert (interne Implementierung, undokumentierte Felder)? Und wie ändert sich der Vertrag (Versionierung, Ankündigungsfristen)? Dieses Dokument ist die Grenze, die dem Platform-Team das Ändern und den Stream-Teams das Verlassen erlaubt.

Versionierung und Deprecation bei internen APIs

Der gefährlichste Trugschluss: „Die Konsumenten sind intern, wir können einfach alle anpassen.” Bei 50 Teams ist „alle anpassen” eine Migration wie bei externen Kunden — nur ohne deren Verständnis, wenn man sie überrumpelt. Deshalb gelten dieselben Regeln wie bei öffentlichen APIs:

  • Additive Änderungen sind frei: neue optionale Felder, neue Endpoints. Darauf optimierst du das Design — lieber ein optionales Feld mit Default als ein Pflichtfeld.
  • Breaking Changes brauchen eine neue Version und eine Koexistenz-Phase. Bei CRDs ist das eingebaut: mehrere Versionen in einer CRD, Conversion Webhooks übersetzen zwischen ihnen, Clients migrieren in ihrem Tempo.
  • Deprecation ist ein Prozess, kein Event: ankündigen, Nutzung messen (wer ruft die alte Version noch auf? — Audit-Log und Metriken machen das bei CRDs trivial), aktiv migrieren helfen, dann erst abschalten. Das ist derselbe Mechanismus wie bei Template-Versionen in Lektion 15 — Plattform-Artefakte altern alle gleich.

Interne APIs haben einen Vorteil, den du nutzen solltest: Du kennst alle Konsumenten. Nutzungs-Telemetrie pro Feld und Version ist machbar und macht Deprecation-Entscheidungen zu Datenfragen statt Vermutungen.

Praxis: eine Plattform-API als CRD entwerfen

Entwirf — auf Papier und dann als YAML — die API für „Team deployt einen Standard-Webservice”. Arbeitsschritte:

  1. Felder sammeln und sortieren: Liste alles, was konfigurierbar sein könnte (Image, Replicas, Ressourcen, Domains, Env-Vars, Probes, Autoscaling, …). Sortiere jedes Feld in: Pflicht / optional mit Default / bewusst nicht exponiert. Begründe die dritte Kategorie — das ist die eigentliche Designarbeit.
  2. CRD schreiben: Formuliere das Ergebnis als CustomResourceDefinition mit OpenAPI-Schema, inklusive Enums und Defaults:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: webservices.platform.acme.io
spec:
  group: platform.acme.io
  scope: Namespaced
  names: { kind: WebService, plural: webservices }
  versions:
    - name: v1alpha1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              required: [image, owner]
              properties:
                image: { type: string }
                owner: { type: string }
                size:
                  type: string
                  enum: [small, medium, large]
                  default: small
  1. Validieren im Cluster: kubectl apply -f crd.yaml in einem kind-Cluster, dann eine Instanz anlegen und absichtlich kaputte Werte testen (size: huge) — die Schema-Validierung lehnt sie ab, ohne dass du eine Zeile Controller-Code geschrieben hast.
  2. Contract-Seite schreiben: Eine halbe Seite: Garantien, Erwartungen, Nicht-Zusicherungen, Änderungsprozess.

Typische Stolperfallen

Leaky Abstraction per Copy-Paste: Wer das Values-Schema des internen Helm-Charts eins zu eins als API exponiert, hat die Implementierung zum Vertrag gemacht — der nächste Chart-Refactor ist dann ein Breaking Change.

1000-Felder-API: Jede Team-Anfrage als neues Feld zu beantworten erzeugt eine API, die niemand überblickt und die Plattform nie wieder vereinfachen kann. Felder hinzufügen ist leicht, entfernen fast unmöglich — also ist Zurückhaltung beim Hinzufügen die einzige wirksame Stelle.

Portal-API-Divergenz: Wenn das Portal eigene Logik enthält statt nur API-Aufrufe zu rendern, entstehen zwei Wahrheiten. Alles, was das Portal kann, muss über die API gehen — Portal und CLI sind Clients.

Kein Status-Kanal: Eine deklarative API ohne aussagekräftigen status (Conditions, Fehlerursachen) zwingt Teams zum Raten, warum ihre Ressource nicht ready wird — und das Raten landet als Ticket beim Platform-Team. Der Status-Teil ist die halbe API.

Interview-Vorbereitung

Auf „Wie designst du die API einer internen Plattform?” antwortest du: Problem (viele inkonsistente Eingänge = zurückkehrender Cognitive Load, keine änderbare Grenze) → Surface (CRDs als deklaratives Fundament, REST für Abfragen, Portal/CLI als dünne Clients, Git-Konventionen explizit als Vertragsbestandteil) → Abstraktionswahl (kuratiertes Schema mit Enums und Defaults statt Durchreichen; Escape Hatch für Sonderfälle) → Evolution (additiv bevorzugen, Versionen mit Koexistenz, Deprecation datengetrieben über Nutzungs-Telemetrie).

Wahrscheinliche Follow-ups:

  • „Warum CRDs statt einer eigenen REST-API?” — Validierung, RBAC, Audit, Versionierung und Reconciliation sind eingebaut; man erbt das Kubernetes-Ökosystem (kubectl, GitOps) statt es nachzubauen. REST ergänzt für synchrone Abfragen.
  • „Wie gehst du mit dem Team um, das ein Feature braucht, das die API nicht hergibt?” — Escape Hatch mit klar reduzierten Garantien; parallel prüfen, ob der Fall häufig genug für eine echte API-Erweiterung ist.
  • „Was macht einen guten Developer Contract aus?” — beidseitige Pflichten, explizite Nicht-Zusicherungen und ein definierter Änderungsprozess; nicht nur das Schema.
  • „Wie deprecatest du ein API-Feld, das noch genutzt wird?” — Nutzung messen, Konsumenten direkt ansprechen, Migrationshilfe liefern, Koexistenzfenster, erst dann abschalten — interne Konsumenten verdienen denselben Prozess wie externe.

Zusammenfassung

Eine Plattform wird erst durch ein definiertes API-Surface zum Produkt: CRDs liefern das deklarative Fundament mit eingebauter Validierung und Versionierung, Portal und CLI bleiben dünne Clients, und der Developer Contract zieht die Grenze zwischen Zusicherung und Implementierungsdetail — die Grenze, die beiden Seiten Bewegungsfreiheit gibt. Abstrahiere auf der Ebene kuratierter Optionen mit Escape Hatch, ändere additiv und behandle Deprecation als gemessenen Prozess.

Mit dem Vertrag ist die Frage „was bietet die Plattform an?” beantwortet. Die nächste Lektion wendet sich der Frage zu, wie sich viele Teams die Plattform teilen: Tenancy-Modelle zwischen Shared, Dedicated und Hybrid.