
TL;DR
|
Du hast die Features geschrieben. Sie laufen lokal. Dann verbringst du die nächsten zwei Stunden mit Dingen, die nichts mit den Features zu tun haben: einem Terraform-Plan, der eine Datenbank ersetzen will, an der du gar nicht gearbeitet hast, einem Kubernetes-Manifest, das eine neue Ingress-Regel benötigt, einer IAM-Richtlinie, der eine Berechtigung fehlt, die für den Deploy nötig ist. Nichts davon gehört zu deiner Aufgabe. Und doch ist all das heute deine Aufgabe.
So sieht diese Liste tatsächlich aus – und warum nichts davon auf deinem Tisch liegen sollte.
Das Wichtigste auf einen Blick: Statusdateien sollten eigentlich nur ein Implementierungsdetail sein. In der Praxis sind sie jedoch eine ständige Quelle für Merge-Konflikte, Abweichungen und stundenlange Debugging-Sitzungen, die nichts mit deinem Anwendungscode zu tun haben.
Du hast keinen Fehler geschrieben. Du führst „terraform plan“ aus und starrst auf einen Diff, der deine Produktionsdatenbank ersetzen will, statt sie zu aktualisieren – sie abreißen und von Grund auf neu aufbauen will, weil jemand letzten Dienstag ein Feld geändert hat (eine Engine-Version, einen Speichertyp, einen Namen), das Terraform nur anwenden kann, indem es die Ressource zerstört und neu erstellt. Niemand hat dir gesagt, dass dieses Feld unveränderlich war. Terraform sagt es dir jetzt, kurz bevor es passiert.
Sperren, Remote-Backends, Workspace-Strategie, Drift-Erkennung: Das sind alles berechtigte Infrastruktur-Anliegen. Es handelt sich dabei nicht um Feature-Entwicklung. Jede Stunde, die du damit verbringst, den Status abzugleichen oder doppelt zu überprüfen, ob der destruktive Diff eines Plans erwartet wurde oder ein schwerwiegender Fehler ist, ist eine Stunde, in der das, was du eigentlich aufbaust, nicht vorankommt.
Das Wichtigste auf einen Blick: Ein Kubernetes-Deployment für einen einzigen Service kann fünf oder sechs separate Ressourcen erfordern, bevor überhaupt etwas läuft. Die Pflege dieser Fläche ist eine eigenständige Disziplin, und sie skaliert mit jedem Service, den du hinzufügst – nicht mit dem Wert, den jeder Service liefert.
Deployment, Service, Ingress oder die neuere Gateway-API mit HTTPRoute, ConfigMap, Secret, vielleicht ein HPA, wenn du automatische Skalierung willst. Egal, ob du diese manuell schreibst oder über Helm-Templates und Kustomize-Overlays verwaltest – der zugrunde liegende Umfang schrumpft nicht, er verlagert sich lediglich von Rohdateien in Template-Logik, die auf ihre ganz eigene Weise versagt: eine falsch ausgerichtete Einrückungsfunktion, die stillschweigend ein Feld auslässt, eine Werteüberschreibung, die nicht dort greift, wo du es erwartet hast. Nichts davon beschreibt, was deine Anwendung tut. All das beschreibt lediglich, wie man einen Container in einem Scheduler zum Laufen bringt.
Das Verhältnis verrät es. Ein Microservice mit 200 Zeilen Geschäftslogik kann 150 Zeilen Kubernetes-Konfiguration (ein grobes, aber gängiges Verhältnis) benötigen, um am Laufen zu bleiben. Das ist kein Komplexitätsbudget, auf das du dich eingelassen hast, als du die Funktion geschrieben hast.
Wichtigste Erkenntnis: Einer Deploy-Pipeline genau die Berechtigungen zu geben, die sie braucht – nicht mehr und nicht weniger –, ist eine Sicherheitsaufgabe. Die meisten Entwickler legen aus Frustration entweder zu weitreichende Berechtigungen fest oder zu enge – und verbringen dann einen Nachmittag damit, einen „Zugriff verweigert“-Fehler zu debuggen, der nichts mit ihrem Programmieren zu tun hat.
Du brauchst die Rolle deiner CI-Pipeline, um Build-Artefakte in einen S3-Bucket hochzuladen. Wenn du die Richtlinie nur auf die Bucket-ARN ausrichtest, schlägt jeder „PutObject“-Aufruf fehl, da Aktionen auf Objektebene den Objektpfad benötigen – also die ARN mit „/*“ am Ende, nicht nur den Bucket selbst. Machst du hier einen Fehler, scheitert die Pipeline mit einem bloßen „AccessDenied“-Fehler, der nichts darüber aussagt, welche von einem Dutzend möglicher Ursachen die tatsächliche ist. Gehst du zu weit in die andere Richtung und gewährst s3:* für jede Ressource im Konto, um den Fehler zu beheben, hast du der Build-Pipeline gerade die Möglichkeit gegeben, auf jeden Bucket zuzugreifen, den dein Unternehmen besitzt.
Das ist echtes Fachwissen. Nur ist es nicht das Fachwissen, für dessen Entwicklung ein Feature-Entwickler eingestellt wurde.
Wichtigste Erkenntnis: Damit zwei Dienste zuverlässig miteinander kommunizieren können, spielen DNS, die Konfiguration des Service-Meshes, Firewall-Regeln und Health-Checks des Load Balancers eine Rolle. Nichts davon taucht in der von dir ausgelieferten Funktion auf. All das kann die Funktion lahmlegen, wenn es falsch ist.
Dein Dienst muss einen anderen Dienst aufrufen. In einer gut geführten Plattform sind das ein Hostname und ein Port. In der Praxis ist es eine Sicherheitsgruppenregel, eine Zielgruppenregistrierung, ein Health-Check-Pfad, der genau den richtigen Statuscode zurückgeben muss, und ein Client irgendwo, der eine zwischengespeicherte Verbindung zu einem Pod hält, der bereits ersetzt wurde, weil das Health-Check-Fenster ein paar Sekunden länger war, als dein Deploy-Skript angenommen hatte.
Nichts davon ist eine Debugging-Fähigkeit, die du dir aneignest, um eine bessere Funktion zu entwickeln. Es ist eine eigenständige Disziplin, die zufällig zwischen dir und der Veröffentlichung liegt.
Das Wichtigste auf einen Blick: Die Geheimnisse jeder Umgebung auf dem neuesten Stand zu halten und sie ohne Ausfallzeiten zu rotieren, ist operative Arbeit, die jedes Mal korrekt erledigt werden muss. Eine einzige übersehene Umgebung ist ein Produktionsvorfall, der nur auf den richtigen Moment wartet.
Eine Datenbank-Anmeldeinformation wird rotiert. Sie wird im Secrets-Manager aktualisiert, und die Zielumgebung übernimmt ihren neuen Wert automatisch beim nächsten Deployment, ohne dass jemand die Werte manuell in die Staging- und Produktivumgebung kopieren muss. Wenn diese automatische Übernahme irgendwo versäumt wird, hast du ein Staging-Deployment, das aus unerklärlichen Gründen keine Verbindung zu irgendetwas herstellen kann – was erst bemerkt wird, wenn das nächste Mal jemand das Staging tatsächlich nutzen muss.
Das Wichtigste zum Mitnehmen: Keine der oben genannten Aufgaben verschwindet, wenn eine Plattform dafür zuständig ist. Sie wird auf eine Ebene verlagert, auf der sie einmalig und einheitlich abgewickelt wird, anstatt von jedem Entwickler bei jedem Projekt neu erfunden zu werden.
Es geht nicht darum, dass diese Arbeit unnötig ist. Zustandsmanagement, Zugriffsbeschränkungen, Netzwerkkonfiguration und die Rotation von Geheimnissen sind allesamt echte Anforderungen für den Betrieb von Software in der Produktivumgebung. Die Frage ist, wer sie erledigt und wie oft.
Wenn die Plattform die Delivery-Ebene verwaltet, erfolgt diese Arbeit einmalig beim Programmieren und gilt automatisch für jede Umgebung. Ein neuer Branch erhält eine produktionsidentische Umgebung, ohne dass jemand dafür ein Terraform-Modul schreiben muss. Eine Plattform, die diese Ebene kontrolliert, legt den Zugriffsbereich für Deploys als Teil des Umgebungsmodells fest, anstatt jeden Entwickler eine IAM-Richtlinie von Grund auf selbst schreiben zu lassen. Ein Geheimnis wird an einer Stelle aktualisiert, und jede Umgebung übernimmt es beim nächsten Deploy, anstatt dass eine Person es manuell von Umgebung zu Umgebung weiterleitet.
Das ist der eigentliche Kompromiss. Nicht weniger Strenge. Die gleiche Strenge, einmal angewendet, anstatt von jedem Entwickler, der mit dem Deployment zu tun hat, neu erfunden zu werden.
Wenn du diesen Monat mehr Zeit mit YAML verbracht hast als mit deinen eigentlichen Features, ist dieses Verhältnis das Signal. Es sagt nichts über deine Fähigkeiten aus. Es zeigt nur, wo die Arbeit liegt.
Lies den Entwicklerleitfaden zum Erstellen von Apps ohne den Aufwand für die Infrastruktur
Muss nicht trotzdem jemand die Plattform selbst konfigurieren?
Ja, aber nur einmal, nicht pro Projekt und nicht pro Entwickler. Der Unterschied liegt darin, ob man eine gemeinsame Bereitstellungsschicht einmalig konfiguriert oder ob jedes Team seine eigene Version davon von Grund auf neu aufbaut. Die Arbeit fällt so oder so an. Was sich ändert, ist, ob sie sich über jedes Projekt hinweg wiederholt oder einmalig zentralisiert wird.
Ist das einfach nur „Infrastructure as Code“ mit zusätzlichen Schritten?
Nein. Terraform- und Kubernetes-Manifeste sind bereits deklarativ, das ist nicht der Unterschied. Der Unterschied besteht darin, ob man auf niedriger Ebene Provisioning-Definitionen erstellt – bei denen man einzelne VPCs, IAM-Rollen und Sicherheitsgruppen noch selbst deklariert – oder eine einzige Anwendungsdefinition auf hoher Ebene erstellt, bei der man beschreibt, was die App benötigt, und die Plattform das darunterliegende Provisioning auf niedriger Ebene übernimmt.
Was ist, wenn mein Team das bereits mit eigenen Tools automatisiert hat? Bei
manchen Teams ist das der Fall, und wenn dein internes Plattformteam diese Ebene bereits gut aufgebaut und gepflegt hat, ist der zusätzliche Nutzen geringer. Das entscheidende Kriterium ist, ob diese internen Tools tatsächlich wie ein Produkt gepflegt werden – mit einem Verantwortlichen und einer Roadmap – oder ob es sich um eine Sammlung von Skripten handelt, die nur eine Person versteht und mit denen alle anderen irgendwie zurechtkommen müssen.
Bedeutet das, dass ich die Kontrolle über meine Infrastrukturentscheidungen verliere?
Nein. Die Dienste, die du nutzt, die Sprache, in der du programmierst, und die Architektur, für die du dich entscheidest, bleiben in deiner Hand. Was sich ändert, ist, wer das Terraform, die Kubernetes-Manifeste und die IAM-Richtlinien schreibt, die diese Entscheidungen tatsächlich umsetzen. Das sind Implementierungsdetails der Bereitstellung, keine Entscheidungen darüber, was du entwickelst.