Warum ist unser Deployment eigentlich immer kaputt?
Das kennt irgendwie jeder Software-Entwickler, es gibt schon wieder Probleme rund um das Deployment. Mal schlägt das Build fehl, obwohl es die ganze Zeit lief, ein anderes Mal ist alles langsam. Und am Ende sitzen alle an der Applikation und klicken wild in der Anwendung herum, um sicherzustellen, das alles funktioniert. Ich denke, hier ist viel im Argen und darum versuche ich in diesem Artikel Licht ins Dunkel zu bringen. Darum schauen wir und in diesem Artikel ein paar Probleme an, die ich immer wieder sehe.
Stichtag, Heute wird deployed
In meiner Laufbahn habe ich viele verschiedene Ansätze für dieses Phänomen gesehen. Für viele Scum-artige Teams ist dieser Tag irgendwann am Ende des Sprints. Bei traditionelleren Modellen häufig kurz bevor die Übergabe an den Kunden oder das andere Team ansteht. Manchmal ist es aber auch einfach nur jeden Dienstag oder Mittwoch.
Fällt hier etwas auf? — Es wird ein Tag festgelegt, nichts weiter!
Oftmals ist das Deployment einfach nur ritualisiert. Es wird halt einfach am Stichtag das Deployment gemacht. Die enthaltenen Änderungen sind oft groß und risikoreich, aber gleichzeitig nicht vollständig getestet.
Interessant dabei ist, dass ich häufig erlebe, dass sich die Teams dessen bewusst sind. Sehr oft versuchen sie auch das Problem zu lösen, hierbei habe ich ein paar Muster immer wieder beobachtet:
Unsere Tickets sind zu groß — wir müssen kleinere Tickets erstellen
Unsere Tickets sind nicht genau genug — die Anforderungen müssen genauer werden
Am Freitag ein Deployment — das ist zu gefährlich, das machen wir nicht
Deployment einen Tag vor Sprint-Ende — das geht immer schief, wir machen das 2 Tage vor Ende des Sprints
Und helfen die Maßnahmen?
Für sich genommen sind diese Maßnahmen häufig sinnvoll. Sie verbessern allerdings selten das Deployment und reduzieren lediglich den Erfolgsdruck.
Das eigentliche Problem ist hier die Bindung an eine konkrete Zeit! Dies erzeugt unnötigerweise Druck, zum Beispiel wenn eine Lösung unbedingt noch in das Deployment muss. Außerdem bildet sich der Eindruck, man könne nur an diesen Tagen deployen. Und andersherum verlassen sich ein paar Entwickler geradezu darauf, dass an diesen Tagen ja besondere Vorsicht herrscht. Wenn solcher Code am Tag des Deployments fehlschlägt, ist er oft schon so alt, dass keiner mehr daran denkt.
So erzeugen diese Deployments an bestimmten Tagen unnötig viel Arbeit und binden viele Entwickler. Das ist viel kostbare Zeit für einen ziemlich kleinen Ertrag.
Kein Stichtag, trotzdem wird deployed
Die Lösung für dieses Problem ist schlicht und einfach häufiger zu deployen. Hierbei müssen wir uns aber wieder mit anderen Fragen auseinandersetzen, wenngleich wir sehen, dass sie schon konkreter sind als vorher.
Wie stellen wir sicher, dass nur das deployed wird auch ansteht?
Was passiert mit unvollständigen Entwicklungen
Ein Rollback kostet uns immer noch sehr viel Zeit und Energie
Testen wir jetzt jeden Tag nach dem Deployment alles
All diese Fragen sollte man detailliert beantworten, weshalb ich darauf in anderen Artikeln genauer eingehen werde. Für den Moment möchte nur ein paar Stichpunkte in den Raum werfen, die zumindest diese Fragen beantworten helfen:
Testpyramide
Test-Driven-Development
Feature-Flags / Feature-Toggle
Forward-Only deployments
Alle herkommen wir müssen die Applikation testen
Auch dieses Verhalten habe ich häufig gesehen. Nach dem Mittagessen treffen sich alle und sprechen sich ab was zu tun ist. Einer muss das Deployment ausführen, wieder jemand checkt, dass die DB noch lebt. Einer hämmert die URL in den Browser und versucht herauszufinden, ob die Applikation noch läuft. Durch lauter Caches sind dabei diverse Requests false-positiv und fallen als Fehler erst später auf. Noch einer checkt die Zahlen, bleiben die Nutzer oder gehen sie? Alle zusammen arbeiten eine Excel-Liste ab, die vermeintlich wichtige Testszenarien enthält.
Insbesondere Teams mit einer sehr schlechten Struktur und unklaren Regeln verfolgen oft diesen Ansatz. Hier fehlt es schlicht und einfach an soliden Tests! Leider führen uns die modernsten Software-Trends noch mehr in diese Richtung. So sind neuere Javascript/Typescript-Frameworks oft kaum getestet und dokumentiert! Auch unsere allgegenwärtige künstliche Intelligenz erstellt oft eher Ducktape-Code und Tests sind fehlerhaft oder fehlen vollends.
Lass die Tests laufen, wir wollen deployen
Um zu verhindern, dass Teams unnötig viel Zeit damit verbrauchen das Deployment zu überwachen sind drei Maßnahmen wichtig:
Die wichtigste Maßnahme sind gute Tests, die vor dem Deployment ablaufen
Ein gutes Monitoring, insbesondere der kritischen Komponenten
Sinnvolle schnelle und gezielte Smoke-Tests nach dem Deployment
Hierbei sind erneut die wichtigsten Stichpunkte, auf die ich jedoch an anderer Stelle detailliert eingehen werde
Testpyramide
Test-Driven-Development
Feature-Flags / Feature-Toggle
Teams, welche nie viele Tests geschrieben haben, verfallen oft in den Irrglauben alles mit E2E-Tests und Smoke-Tests abbilden zu müssen. In der Folge läuft dann eine Testsuite in Selenium / Playwright / Ghostinspector die ganze Zeit mit und versucht alle Einzelheiten zu erfassen. Diese Art zu testen ist ein klassischer Testing-Cone und ist eine sehr, sehr schlechte Praxis. |
Das Deployment wird vom Deployment-branch gebaut
Diese Angewohnheit ist mir nicht so oft über den Weg gelaufen, aber sie kommt immer mal wieder vor.
Hierbei arbeiten Teams auf einem Branch innerhalb ihres Revisions-Control-Systems, z.b. develop
aber das Deployment auf verschiedenen Systemen wird von anderen Branches aus gemacht, z.b. main
für die Produktion, order release
für Staging.
Hier lauert jedoch ein besonders hinterlistiges Problem, denn genau genommen wird so häufig Code in der Produktion ausgeführt werden kann, der so nie getestet wurde!
Besonders ärgerlich ist dabei, dass mehrere prominente Strategien, z.b. Gitlab-Flow, dieses Vorgehen propagieren, oder zumindest nicht einfach erklären.
Was ist das genaue Problem?
Das Problem ist, dass es hier 2 Bau-Prozesse des gleichen (nicht desselben) codes geben kann, die zu unterschiedlichen Artifakten führen.
Häufig habe ich beobachtet, dass der Code von den Entwicklern auf einem Branch (development
) eingereicht wird.
Anschließend wird periodisch dieser Branch in einem anderen, z.b. main
zusammegeführt (merge).
Hierbei wird der Code dann ein zweites Mal gebaut und dieser Code dann deployed.
Manchmal scheitert dieser 2 Prozess, z.B. weil sich die Runtime geändert hat.
Und dann fällt auf, dass das Deployment davon abhängt, zweimal erfolgreich alles zu bauen!
Das Deployment wird aus einem Artefakt erzeugt
Hier ist die Lösung am komplexesten, denn hier müssen ganze Prozesse angepasst werden. Für den meisten Teams ist ein Prozess der auf Branches fusst einfach nicht gut. In aller Regel ist es besser einen einfacheren Ansatz zu wählen, wobei Trunk-based hier das wichtigste Stichwort ist. Wobei man sich auch an Microsofts Strategie anlehnen kann.
Weiter muss der CI/CD-Prozess angepasst werden, sodass jeder Bau-Vorgang ein valides Artefakt erzeugt, welches anschließend nicht mehr verändert wird. Wichtig ist hierbei, dass prinzipiell jeder erfolgreiche Build ein valides Deployment darstellen kann! Wie dieser Umbau im Detail aussehen kann, stelle ich jedoch an anderer Stelle vor
Ein Artefakt bezeichnet ein Archiv, mit allem, was zum Deployment notwendig ist. Ein typisches Artefakt kann z.B. ein Docker-Image sein, oder eine jar-Datei für Java-Projekte, sowie eine zip-Datei, welche die gesamte Website enthält, inkl. Bildern und anderes Assets. |
Abschluss
Ich habe hier mit Sicherheit nicht alles beschrieben, weshalb ein Deployment scheitern kann. Dennoch hoffe ich ein paar Denkanstöße geliefert zu haben, ohne zu sehr den Spiegel vorzuhalten.
Ich plane zu dem riesigen Thema Deployments eine Serie von Artikeln zu schreiben, um genauer auf einzelne Aspekte einzugehen.