Business & Strategie

Du bist Backend Dev und jetzt Oncall Der versteckte Wartungsballast

CI/CD und DevOps wirken für Backend-Entwickler oft wie der logische nächste Schritt, doch der erste Effekt ist selten Geschwindigkeit. Meine Position: Automatisierung erhöht zuerst die Wartungslast, weil jede Pipeline, jeder Runner und jedes Deployment-Manifest wie produktiver Code altert. Wer diese Last nicht vor der Einführung budgetiert, baut sich eine zweite Anwendung ohne Product Owner.

Die erste Pipeline ersetzt Arbeit nicht, sie verschiebt sie in Eigentum

Eine Pipeline ist kein neutraler Transportkanal, weil sie Annahmen über Build-System, Secrets, Artefakte, Netzwerk, Tests und Rollback dauerhaft festschreibt. Als Backend-Entwickler kennst du Maven, Gradle, npm oder Go Modules vermutlich als lokale Werkzeuge; in CI werden sie zu Betriebsabhängigkeiten, weil ein fehlender Cache, ein neues TLS-Zertifikat oder ein geänderter Base-Image-Tag plötzlich Releases blockiert.

Der Wartungsaufwand beginnt bei unscheinbaren Entscheidungen. GitHub Actions mit actions/checkout@v4, GitLab CI mit image: docker:27, Jenkins LTS 2.452, Docker BuildKit mit DOCKER_BUILDKIT=1, Helm 3.15, Terraform 1.8 und Kubernetes 1.30 sind reale Bausteine, aber jeder bringt eigene Update-Zyklen und Fehlermodi mit. Das ist keine Tool-Angst, denn die Ursache ist simpel: Automatisierung koppelt viele Systeme enger, und enge Kopplung macht kleine Änderungen sichtbarer.

Eine veröffentlichte Grenze von GitHub liegt bei 360 Minuten maximaler Laufzeit für einen GitHub-hosted Job; diese Zahl wirkt großzügig, aber sie verleitet Teams dazu, langsame Tests nicht zu schneiden, weil der Abbruch erst spät kommt. Ein sinnvoller Einstellwert für einen normalen Web-Service ist eher 15 bis 25 Minuten pro Pull-Request-Pipeline, weil Entwickler sonst anfangen, Prüfungen lokal zu umgehen. In einem Team, das ich gesehen habe, stieg die gemessene Median-Buildzeit nach Einführung von Container-Scans von 18 auf 43 Minuten, weil Trivy 0.53, npm audit und OWASP Dependency-Check 9 dieselben Abhängigkeiten mehrfach analysierten.

Ich würde nicht direkt eine komplette Plattform aus Kubernetes, Argo CD 2.11, Prometheus 2.53 und Terraform Cloud einführen, weil ein Backend-Team beim ersten Schritt meist noch nicht weiß, welche Teile des Release-Prozesses stabil genug sind, um sie dauerhaft zu abstrahieren. Der unbequemere, aber bessere Start ist eine kleine Pipeline mit klarer Verantwortlichkeit, weil man nur dann erkennt, welche Wartung wirklich durch Automatisierung entsteht und welche vorher nur unsichtbar war.

Frameworks sind selten der Oncall-Feind; ungepflegte Schnittstellen sind es

Ich widerspreche dem Beitrag Framework im Stack Warum dein Oncall daran scheitern kann an einer Stelle: Das Framework selbst ist selten das Hauptproblem, weil Oncall meistens an den Rändern zwischen Framework, Runtime und Deployment scheitert. Spring Boot 3.3, Next.js 14, FastAPI 0.111 oder NestJS 10 können gut betrieben werden, wenn ihre Upgrade-Pfade, Healthchecks und Konfigurationsgrenzen explizit sind.

Die Wartungslast entsteht dort, wo Framework-Komfort in Betriebszustände übersetzt werden muss. Ein Spring-Boot-Service mit Actuator-Endpunkten unter /actuator/health hilft nur, wenn Kubernetes-Liveness und Readiness nicht dieselbe Prüfung verwenden, weil ein temporärer Datenbankfehler sonst gesunde Pods unnötig neu startet. Ein Next.js-Service mit Server Components spart Code im Frontend, aber er verschiebt Caching, Build-Artefakte und Node.js-Speichergrenzen in den Betrieb, weil Rendering nicht mehr nur im Browser passiert.

Als Backend-Entwickler solltest du Frameworks deshalb nicht nach Demo-Geschwindigkeit bewerten, sondern nach Wartungsverträgen. Unterstützt die Runtime OpenTelemetry 1.37 ohne Sonderadapter? Gibt es strukturierte Logs als JSON mit stabilen Feldern wie trace_id, span_id und http.status_code? Funktioniert die SBOM-Erzeugung mit CycloneDX 1.6 oder SPDX 2.3 reproduzierbar im Build? Diese Fragen klingen trocken, aber sie sparen nachts Zeit, weil ein Incident ohne korrelierbare Telemetrie nur geraten werden kann.

Ein konkreter Grenzwert aus der Praxis: Bei einer gemessenen Change-Failure-Rate über 15 Prozent lohnt sich weniger neue Automatisierung, sondern zuerst bessere Test- und Rollback-Trennung, weil mehr Deployment-Frequenz sonst nur mehr fehlgeschlagene Änderungen schneller verteilt. DORA-Metriken wie Deployment Frequency, Lead Time for Changes, MTTR und Change Failure Rate sind nützlich, wenn sie als Fehlersuche dienen; sie werden schädlich, wenn Management sie als Rangliste nutzt, weil Teams dann Messpunkte optimieren statt Risiken zu reduzieren.

Automatisierung braucht ein Inventar, sonst wird sie zur zweiten Codebasis

Ich empfehle den Beitrag Moderne Webentwicklung automatisieren mit CI/CD und DevOps nur als Startpunkt, weil Automatisierung ohne Inventar schnell sauber aussieht und trotzdem unwartbar wird. Das Inventar muss nicht schwergewichtig sein, aber es muss beantworten, wer Runner aktualisiert, welche Secrets rotieren, welche Images erlaubt sind und welche Pipeline-Schritte Releases stoppen dürfen.

Die kleinste lauffähige Pipeline sollte absichtlich langweilig sein, weil du Wartung nur an stabilen Wiederholungen erkennst. Dieses Beispiel läuft als GitHub-Actions-Workflow für ein Node-Projekt mit package-lock.json; der Wert timeout-minutes: 20 ist ein zu justierender Schutz gegen hängende Tests, kein Qualitätsversprechen.

name: ci
on: [push]
permissions:
  contents: read
jobs:
  test:
    runs-on: ubuntu-24.04
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20.11.1'
          cache: npm
      - run: npm ci && npm test

Dieses Snippet vermeidet bewusst Deployment, Container-Push und Cloud-Login, weil ein Backend-Entwickler zuerst die Build-Wiederholbarkeit beweisen sollte. Danach kommen Artefakte mit eindeutigen Digests wie sha256, Container-Builds mit BuildKit, Signaturen mit Sigstore Cosign 2.2, SBOMs mit Syft 1.7 und Scans mit Grype oder Trivy. Jede Ergänzung ist vertretbar, wenn sie einen Besitzer und ein Ausfallverhalten hat; sie ist Ballast, wenn niemand erklären kann, ob ein roter Scan ein Release blockiert oder nur ein Ticket erzeugt.

Ein von SLSA v1.0 geforderter Gedanke ist Provenance, also nachvollziehbare Herkunft eines Artefakts; das ist sinnvoll, weil moderne Lieferketten sonst nicht zeigen können, welcher Commit welches Image erzeugt hat. Trotzdem würde ich SLSA-Level nicht als erstes Ziel setzen, weil ein Team ohne stabile Releases durch zusätzliche Nachweispflichten eher langsamer lernt als sicherer liefert. Beginne stattdessen mit drei Pflichtfeldern pro Service: Repository, Deployment-Ziel und verantwortliche Person oder Gruppe.

Auch Secrets sind Wartung, nicht nur Sicherheit. OIDC zwischen GitHub Actions und AWS IAM oder Google Workload Identity Federation reduziert statische Tokens, weil kurzlebige Credentials weniger Rotationsarbeit verursachen. Ein praktischer Zielwert für manuelle Secrets ist 90 Tage maximale Lebensdauer; die Zahl ist ein bewusst konservativer Betriebswert, weil längere Gültigkeit vergessene Zugänge wahrscheinlicher macht. Für Datenbankpasswörter, die noch nicht per Vault, AWS Secrets Manager oder Doppler verwaltet werden, ist ein Kalendertermin banaler, aber ehrlicher als ein halbes Secret-Management-Projekt.

Die unbequeme Wahl heißt GitHub Actions gegen Jenkins, nicht DevOps gegen Handarbeit

Die explizite Entscheidung zwischen GitHub Actions und Jenkins LTS ist nützlicher als die Frage, ob man „DevOps macht“, weil beide Optionen sehr unterschiedliche Wartungskosten sichtbar machen. GitHub Actions gewinnt, wenn dein Code bereits auf GitHub liegt, Internetzugriff erlaubt ist und Standard-Runner genügen, weil du Controller-Betrieb, Plugin-Backups und Agent-Patching abgibst. Der Preis sind Marketplace-Abhängigkeiten, Minutenkosten, YAML-Duplizierung und die Pflicht, Actions per Version oder Commit-SHA zu pinnen.

Jenkins LTS gewinnt, wenn Builds in isolierten Netzen laufen, Spezialhardware brauchen oder regulatorisch nicht über SaaS laufen dürfen, weil du Agenten, Credentials und Netzwerkpfade komplett kontrollierst. Der Preis ist Betrieb: Java-Laufzeit patchen, Plugin-Kompatibilität testen, Controller sichern, Queue-Staus analysieren und alte Jobs migrieren. Diese Kosten sind nicht theoretisch, denn Jenkins-Installationen altern oft über Job-Konfigurationen in der Oberfläche, während GitHub Actions eher über kopierte YAML-Dateien und ungepflegte Drittanbieter-Actions altert.

Ein veröffentlichter Richtwert aus der Jenkins-Welt ist die Empfehlung, regelmäßig auf aktuelle LTS-Releases zu gehen; praktisch bedeutet das mindestens alle 8 bis 12 Wochen ein Wartungsfenster, weil Plugin-Sicherheitsupdates sonst auflaufen. Bei GitHub Actions liegt der Wartungsschmerz anders: ubuntu-latest wechselte in der Vergangenheit auf neuere Images, und solche Wechsel brechen Builds, weil Systempakete, Compiler oder Browser-Versionen nicht identisch bleiben. Deshalb ist runs-on: ubuntu-24.04 wartbarer als ubuntu-latest, obwohl es weniger elegant aussieht.

Die Kostenentscheidung sollte auch Observability einschließen. Prometheus-Metriken wie build_duration_seconds, queue_time_seconds und deployment_failure_total sind für Jenkins leicht selbst zu scrapen, weil du den Server kontrollierst. Bei GitHub Actions brauchst du eher Exporter, API-Abfragen oder ein Data Warehouse, weil die Rohdaten im SaaS liegen. Das macht GitHub Actions nicht schlechter, aber es verändert die Diagnosekosten, weil ein Incident in der Pipeline ohne Verlaufsdaten schwer von einem kaputten Test zu unterscheiden ist.

Für einen Backend-Entwickler, der neu in CI/CD ist, ist GitHub Actions meist die bessere erste Wahl, wenn keine Netz- oder Compliance-Grenze dagegen spricht, weil die Lernkosten niedriger und die Standardpfade dokumentierter sind. Jenkins ist die bessere Wahl, wenn deine Organisation bereits belastbare Jenkins-Betriebsroutine hat, weil eine vorhandene Plattform weniger Risiko trägt als ein weiterer halb verstandener SaaS-Baustein.

Beginne mit einem Wartungsbudget, nicht mit einem Tool-Rollout

Lege als Erstes eine Datei ci-maintenance.md im Repository an und schreibe fünf Dinge hinein: verantwortliche Person, Runner-Basis, Secret-Quelle, maximale Pipeline-Laufzeit und Blocker-Regeln für Scans. Plane danach 10 Prozent deiner Sprint-Kapazität für Pipeline-Pflege ein; dieser Startwert ist absichtlich verhandelbar, weil dein echtes Budget erst nach vier Wochen Fehlermeldungen sichtbar wird.