Ein Refactoring kann im Test sauber wirken und in Produktion trotzdem den Betrieb verschlechtern: Der neue Code antwortet, aber der Rückweg ist blockiert oder Telemetrie enthält plötzlich Nutzdaten. Für DevOps-Teams ist deshalb die alte Empfehlung „kleine Änderungen häufig deployen“ allein zu dünn geworden. Meine Position: Seit 2024 sollte ein Legacy-Umbau erst dann live gehen, wenn Beobachtbarkeit und Rücknahme für genau diese Änderung nachgewiesen sind.
Kleine Deployments sind ohne geprüften Rückweg kein Sicherheitsnetz
Dem Beitrag Refactoring 2026 Welche Ratschlaege Tech Leads streichen sollten widerspreche ich an einem Punkt: Nicht die Größe des Commits entscheidet zuerst über das Produktionsrisiko, sondern ob sich sein Effekt isolieren und zurücknehmen lässt. Ein Deployment mit zehn geänderten Zeilen kann einen Datenbankwert anders interpretieren; ein späteres Zurückrollen des Containers stellt den alten Wert nicht wieder her. „Macht kleine Schritte“ bleibt ein guter Entwicklungshinweis, ist aber kein vollständiger Betriebsplan.
Was sich in den vergangenen zwei Jahren für viele Teams verändert hat, ist die Zahl der getrennt auslieferbaren Teile: Neben der Anwendung werden Instrumentierung, Collector-Konfiguration, Deployment-Manifest und Datenbankschema oft unabhängig geändert. Das vergrößert die Menge möglicher Zustände, weil ein erfolgreiches kubectl rollout status nur den Kubernetes-Rollout bestätigt, nicht die Kompatibilität dieser Teile. Kubernetes dokumentiert für progressDeadlineSeconds einen Standardwert von 600 Sekunden; diese Herstellerangabe beschreibt, wann ein Deployment als nicht fortschreitend gilt, nicht wie lange fachliche Fehler unentdeckt bleiben dürfen.
Ich würde ein Schema-Refactoring nicht gemeinsam mit dem Entfernen des alten Lesepfads ausrollen, weil ein Container-Rollback dann auf Daten treffen kann, die er nicht mehr versteht. Stattdessen bleibt die alte Leselogik zunächst aktiv, während neue Schreibvorgänge ein kompatibles Format erzeugen. Erst nach einer überprüften Übergangsphase wird der alte Pfad entfernt. Wer PostgreSQL nutzt, muss dabei auch DDL und Anwendung getrennt betrachten: Eine rückwärtskompatible neue Spalte ist etwas anderes als eine umbenannte Spalte, auf die der vorige Release noch zugreift.
Vor dem Rollout gehören drei Fragen ins Runbook: Welche Version kann auf die bereits geschriebenen Daten zugreifen? Welcher Schalter stoppt ausschließlich den neuen Pfad? Wer entscheidet anhand welcher Messung über die Rücknahme? Ein Kubernetes-Deployment-Undo beantwortet nur die zweite Frage, sofern die Funktion nicht zugleich in PostgreSQL, Redis oder einer Queue Nachrichten mit neuem Format hinterlassen hat. Für Apache Kafka gilt dasselbe Prinzip: Ein alter Consumer kann nach dem Rollback weiterhin neue Events lesen müssen, weshalb ein Event-Schema und seine Kompatibilitätsregel Teil der Freigabe sind.
Ein grüner Rollout misst nicht, was Nutzer tatsächlich erleben
Die Betriebsprobe sollte nach dem Deployment beginnen, nicht mit dessen Statusmeldung enden. Das folgende Skript läuft mit bash und kubectl gegen einen erreichbaren Cluster; Namespace, Deployment und Pod-Label müssen zur eigenen Umgebung passen. Es zeigt bewusst nur den infrastrukturellen Zustand. Fachliche Freigabe lässt sich aus seiner Ausgabe nicht ableiten.
#!/usr/bin/env bash
set -euo pipefail
ns=legacy
app=bestand-api
kubectl -n "$ns" rollout status "deployment/$app" --timeout=120s
kubectl -n "$ns" get deployment "$app" \
-o jsonpath='{.status.availableReplicas}{"\n"}'
kubectl -n "$ns" get pods -l "app=$app" -o wide
kubectl -n "$ns" get events --field-selector type=Warning
Den Status ergänze ich um eine Messung auf Anfrageebene. Prometheus kann Fehlerquoten aus http_server_request_duration_seconds_count berechnen, sofern Anwendung und Exporter diese Metrik tatsächlich liefern. Als anzupassenden Startwert würde ich eine Fehlerquote von 2 Prozent für den neuen Pfad prüfen; der Wert ist keine allgemeine SLO-Empfehlung, weil eine selten genutzte Verwaltungsfunktion und ein stark frequentierter Leseendpunkt unterschiedliche Fallzahlen haben. Vor dem Alarm muss deshalb feststehen, welche Route, Methode und Release-Kennung in Zähler und Nenner eingehen.
Auch Latenz braucht einen Vergleich, der den Umbau trifft. Ein globaler p95-Wert kann stabil bleiben, obwohl der neue Pfad langsamer wird, weil der alte Pfad den Großteil der Anfragen bedient. OpenTelemetry kann Spans mit einer Kennung für den ausgelieferten Pfad versehen; Grafana kann daraus getrennte Ansichten für alten und neuen Pfad anzeigen. Die Kennung sollte eine kleine feste Wertemenge haben, etwa alt und neu, weil eine Nutzer-ID als Metriklabel die Kardinalität und damit Speicherbedarf und Abfragekosten erhöht.
Das ist der Punkt, an dem „mehr Observability“ als pauschaler Rat veraltet ist. Mehr Daten helfen nur, wenn sie eine konkrete Rücknahmeentscheidung ermöglichen. Ein Dashboard mit CPU, Speicher und Pod-Restarts erkennt keinen falsch berechneten Betrag, solange die Anwendung technisch gesund bleibt. Für diesen Fall braucht das Team eine fachliche Gegenprobe, etwa die Abweichung zwischen alter und neuer Berechnung auf demselben zulässigen Testdatensatz. Der zugehörige Alarm muss einer Person und einer Handlung zugeordnet sein; andernfalls erzeugt er bei einem nächtlichen Rollout lediglich zusätzlichen Lärm.
Automatische Traces machen Datenminimierung zur Release-Frage
Den Beitrag Legacy umbauen ohne Budget und ploetzlich DSGVO-Risiko lese ich als Warnung vor einer realen Nebenwirkung, würde den Schwerpunkt für den Betrieb aber anders setzen: Das erste Kontrollziel ist der Weg der Telemetrie aus dem Cluster. Wer bei einem Umbau OpenTelemetry-Autoinstrumentierung aktiviert oder den OpenTelemetry Collector an einen externen OTLP-Endpunkt anschließt, verändert diesen Weg auch dann, wenn die fachliche Datenbank unangetastet bleibt.
Die frühere Empfehlung „Trace erst einmal alles, filtern können wir später“ würde ich nicht übernehmen, weil ein bereits exportierter Span durch eine nachträgliche Filterregel nicht zurückgeholt wird. Bei HTTP-Instrumentierung sind insbesondere URLs, Query-Parameter, Header und Fehlertexte zu prüfen; ob sie erfasst werden, hängt von SDK, Bibliothek und Konfiguration ab. Ein Test mit absichtlich markierten Beispieldaten zeigt zuverlässiger als eine Architekturzeichnung, welche Attribute tatsächlich beim Empfänger ankommen. Die Prüfung muss vor dem ersten Export in die Zielumgebung stattfinden.
Im Collector lassen sich Attribute mit dem attributes-Processor entfernen und Datensätze mit dem filter-Processor anhand von OTTL-Regeln verwerfen. Diese Maßnahmen wirken jedoch nur auf Daten, die den betreffenden Collector passieren, weshalb direkte SDK-Exports und zusätzliche Agenten inventarisiert werden müssen. Ebenso wenig ersetzt Tail Sampling die Datenminimierung: Es entscheidet nach Empfang eines Traces über dessen Weitergabe, sodass unerwünschte Inhalte vorher bereits im Collector verarbeitet wurden. Bei mehreren Collectors braucht Tail Sampling außerdem eine passende Zuordnung zusammengehöriger Spans, sonst wird die Entscheidung auf unvollständiger Sicht getroffen.
Als lokal festzulegenden Prüfwert schlage ich für den Exporttest 20 Minuten vor: In diesem Fenster werden markierte Requests ausgelöst und anschließend am tatsächlichen Zielsystem gesucht. Die Dauer ist zu verlängern, wenn Batching oder Warteschlangen eine spätere Ankunft ermöglichen. Ein leerer Treffer allein genügt nicht, weil auch ein kaputter Export leer bleibt; parallel muss ein harmloses Kontrollattribut nachweisbar ankommen. Erst diese Kombination belegt, dass die unerwünschten Daten fehlen und die Pipeline trotzdem funktioniert.
Die DSGVO-Frage ist damit keine Aufgabe, die nach dem Deployment ausschließlich an eine andere Stelle abgegeben werden kann. Artikel 5 Absatz 1 Buchstabe c verlangt Datenminimierung; welche Attribute erforderlich sind, muss fachlich geklärt werden. Das DevOps-Team kann dazu die überprüfbare technische Grenze liefern: welche Quellen senden, welche Prozessoren greifen und bei welchem Empfänger die Daten landen. Ohne diese Bestandsaufnahme bleibt selbst eine gut formulierte Aufbewahrungsregel unvollständig, weil sie einen unbekannten zweiten Exportpfad nicht erfassen kann.
Canary und synthetischer Replay gewinnen unter verschiedenen Bedingungen
Für die Freigabe eines neuen Pfads stehen zwei brauchbare Verfahren gegeneinander. Ein Canary mit echten Anfragen gewinnt, wenn nur Produktionsverkehr die relevanten Lastspitzen und Integrationen zeigt; sein Preis ist, dass echte Nutzer Fehler sehen können und eine schnelle Umschaltung nötig ist. Ein synthetischer Replay mit bereinigten Fixtures gewinnt, wenn Ausgaben vorab verglichen werden sollen, ohne reale personenbezogene Nutzdaten zu kopieren; sein Preis ist eine Lücke zwischen Testmustern und tatsächlichem Verkehr. Keines der Verfahren ersetzt das andere, weil sie unterschiedliche Unsicherheiten verkleinern.
Beim Canary ist der entscheidende Schalter nicht zwingend das Deployment selbst. Ein Feature-Flag, beispielsweise über OpenFeature angebunden, kann den neuen Pfad für einen begrenzten Anteil aktivieren, während beide Codepfade im selben Release bleiben. Als zu kalibrierenden Anfangsanteil würde ich 1 Prozent wählen; bei geringem Verkehrsaufkommen liefert dieser Anteil zu wenige Beobachtungen, weshalb dann eine gezielte interne Kohorte sinnvoller ist. Vorher muss getestet werden, ob das Abschalten des Flags auch laufende Jobs, Queue-Consumer und zwischengespeicherte Entscheidungen erreicht. Sonst wirkt der Schalter nur auf neue HTTP-Anfragen.
Für den Replay sollten Fixtures aus genehmigten Testfällen stammen oder so bearbeitet sein, dass keine Rückschlüsse auf echte Personen möglich sind. Eine bloß andere Kennung reicht nicht, wenn Freitext oder seltene Kombinationen erhalten bleiben. Ergebnisse lassen sich über stabile fachliche Felder vergleichen; Zeitstempel, zufällige IDs und nicht relevante Reihenfolgen werden vorher normalisiert. Dieser Aufwand kostet Entwicklungszeit, liefert aber eine präzise Antwort auf die Frage, welche Ausgabe der Umbau verändert hat, statt nur eine gestiegene Fehlerrate zu melden.
Ich würde eine Canary-Freigabe nicht allein an „keine Pager-Meldung“ knüpfen, weil ein fehlender Alarm auch aus einer defekten Metrikpipeline stammen kann. Zur Freigabe gehören ein sichtbar eintreffender Kontrollrequest, die getrennte Messung von altem und neuem Pfad sowie ein erfolgreich geprobter Rückschalter. Wenn eines davon fehlt, bleibt der Canary klein oder wird beendet. Das ist eine strengere Regel als „deployen und beobachten“, aber für den Bereitschaftsdienst ist eine nachweisbare Abbruchmöglichkeit wertvoller als ein weiteres grünes Deployment-Symbol.
Der nächste Umbau beginnt mit einem Rücknahmetest
Nimm beim nächsten geplanten Legacy-Release zuerst den vorgesehenen Rückschalter und teste ihn in einer produktionsnahen Umgebung: Sende eine markierte Anfrage über den neuen Pfad, schalte zurück und prüfe Antwort, Datenformat und Trace am tatsächlichen Empfänger. Halte fest, was danach nicht rückgängig zu machen ist. Falls dieser Test scheitert, verschiebe nicht bloß den Alarmwert; ändere den Auslieferungsplan, bevor der Bereitschaftsdienst ihn nachts reparieren muss.



