- In Betriebsteams bleiben manuelle Abläufe (Toil) übrig, die sich nur schwer vollständig beseitigen lassen, etwa Infrastrukturänderungen oder Account-Provisionierung; mit wachsendem Unternehmen nehmen Schritte und Ausnahmen zu
- Auch wenn jeder einzelne Schritt automatisierbar erscheint, entstehen bei nur teilweiser Skriptierung immer mehr Einzweck-Tools, und Nutzer müssen weiterhin lange Verfahrensdokumente befolgen
- Ein Do-nothing-Skript führt die eigentliche Arbeit nicht automatisch aus, sondern kapselt jeden Schritt des Ablaufs in eine Funktion und weist den Nutzer Schritt für Schritt an
- Nutzer verlieren seltener die aktuelle Position oder überspringen Schritte, und Entwickler können später die Anleitung für einen bestimmten Schritt leichter durch echten Automatisierungscode ersetzen
- Der unmittelbare manuelle Aufwand sinkt dadurch nicht, aber die Anfangskosten der Automatisierung werden reduziert, sodass Toil im Lauf der Zeit schrittweise abgebaut werden kann
Wenn manuelle Abläufe zur Plackerei werden
- Jedes Betriebsteam hat noch manuelle Abläufe, die bislang nicht automatisiert sind, und Toil lässt sich nur schwer vollständig beseitigen
- In wachsenden Unternehmen werden Abläufe wie Infrastrukturänderungen oder die Provisionierung von Benutzerkonten leicht zu einem zentralen Toil-Faktor
- Ein Ablauf zur Provisionierung von Benutzerkonten kann etwa die folgenden Schritte umfassen
- SSH-Schlüsselpaar für den Nutzer erzeugen
- Öffentlichen Schlüssel in Git committen und nach master pushen
- Abschluss des Build-Jobs abwarten
- E-Mail-Adresse des Nutzers im Mitarbeiterverzeichnis nachschlagen
- Privaten Schlüssel per 1Password an den Nutzer übermitteln
- In realen Umgebungen kann ein Ablauf auf bis zu 20 Schritte anwachsen, oder man muss während der Ausführung fortlaufend Verzweigungen und Sonderfälle im Blick behalten
- Solche Arbeiten erfordern viel Konzentration, fühlen sich aber eher wie das Abhaken einer weiteren Checkbox an als wie interessante Problemlösung und werden dadurch zum Slog
Die Lücken, die Teilautomatisierung hinterlässt
- Slog wirkt wie ein guter Kandidat für Automatisierung
- Für jeden einzelnen Schritt lässt sich leicht eine Automatisierungsmöglichkeit vorstellen
- Computer können Anweisungen schneller und genauer ausführen als Menschen
- Auch die Wahrscheinlichkeit für Practical Drift ist geringer
- Das Problem ist, dass die Automatisierung von Slog oft nach alles oder nichts klingt
- Man kann zwar ein Skript bauen, das nur Schritt 2 oder Schritt 5 erledigt, doch die Umständlichkeit des gesamten Ablaufs nimmt dadurch kaum ab
- Wenn Einzweck-Skripte zunehmen, unterscheiden sich Konventionen und erwartetes Verhalten von Tool zu Tool, und Nutzer müssen weiterhin mehrstufige Dokumentationen befolgen
Wie ein Do-nothing-Skript funktioniert
- Fast jeder Slog lässt sich in ein Do-nothing-Skript umwandeln
- Der Kern besteht darin, die Anweisungen des Slogs in Code zu übertragen und jeden Schritt in einer eigenen Funktion zu kapseln
- Der Ablauf eines Beispielskripts sieht so aus
CreateSSHKeypairStepgibt den Befehlssh-keygenaus und wartet, bis der Nutzer Enter drücktGitCommitStepweist an, den öffentlichen Schlüssel in das Git-Repository zu kopieren und anschließendgit commitsowiegit pushauszuführenWaitForBuildStepweist an, unter der URL des Build-Jobs auf den Abschluss zu wartenRetrieveUserEmailStepsucht die E-Mail-Adresse im Verzeichnis, nimmt sie als Eingabe entgegen und speichert sie incontext["email"]SendPrivateKeyStepweist an, in 1Password ein Dokument für den privaten Schlüssel zu erstellen und es mit dem Nutzer dieser E-Mail-Adresse zu teilen
- Dieses Skript führt keinen Schritt des Ablaufs tatsächlich aus, sondern gibt dem Nutzer Schritt für Schritt Anweisungen und wartet auf die manuelle Bestätigung der Fertigstellung
Der Weg zur Automatisierung
- Auf den ersten Blick kann ein Do-nothing-Skript wirken, als mache es die Dokumentation nur schwerer lesbar; tatsächlich hält es den Workflow aber sicherer zusammen
- Die Wahrscheinlichkeit sinkt, dass Nutzer ihre aktuelle Position verlieren oder Schritte überspringen
- Es wird leichter, konzentriert zu bleiben und den Slog bis zum Ende durchzuführen
- Jeder Schritt ist als Funktion getrennt, sodass der Text eines bestimmten Schritts durch Code mit echter Funktion ersetzt werden kann
- Mit der Zeit entsteht eine Bibliothek wiederverwendbarer Schritte, und spätere Automatisierungsarbeit wird effizienter
- Ein Do-nothing-Skript selbst reduziert nicht den manuellen Arbeitsaufwand des Teams
- Seine Wirkung besteht darin, einen Zustand zu schaffen, in dem Automatisierung leichter begonnen werden kann; darauf aufbauend kann das Team Toil im Lauf der Zeit abbauen
1 Kommentare
Hacker-News-Meinungen
Ich mag diesen Ansatz wirklich sehr.
Im Großen und Ganzen ist es eine weitere Art, eine Schnittstelle um einen Prozess herum zu definieren. Dieser Prozess kann manuell sein oder automatisiert werden, aber die Schnittstelle kann gleich bleiben. Deshalb ist das ziemlich mächtig, wenn man Schritte automatisiert.
Man kann es genauso anwenden wie bei anderen Systemen.
Früher habe ich Google Sheets manuell befüllt und das dann per Skript automatisiert; ich habe auch schon Jira-Tickets erstellen lassen, die dann automatisch aufgegriffen und verarbeitet wurden. Man kann schneller anfangen, nur die nervigsten Teile automatisieren und muss nicht zwingend alles automatisieren.
Ein Nebeneffekt von Skripten, die nichts tun, ist, dass sie eher tatsächlich genutzt werden als Dokumentation und deshalb häufiger aktuell gehalten werden können.
Gegen eine Liste von Skripten, die häufige Aufgaben automatisieren, habe ich nichts. Aber in dem Moment, in dem diese Skripte von produktiven Diensten aufgerufen werden, entsteht eine enorme technische Schuld für die unglückliche Person, die dieses Spaghetti-Geflecht später entwirren muss.
Darüber hinaus hat der menschenzentrierte Prozess dann das System in Code gegossen, sodass Änderungen, die für das Softwaresystem vorteilhaft wären — zum Beispiel das System in Komponenten aufzuteilen — unmöglich werden. Am Ende bedeutet schrittweise Erweiterung, immer mehr in den Prozess hineinzuschieben, was zu einem sich selbst verstärkenden Kreislauf wird, der immer weiter an den Skript-Monolithen anbaut.
Während man 3 und 4 durchführt, lässt man den Prozess wiederholt und von möglichst vielen Personen nutzen und passt ihn an den Missbrauch an, der sich in der Praxis zeigt.
4) den Prozess automatisieren.
Jeder stimmt 1 und 2 zu, und die meisten ziehen am Ende auch bei 4 mit, aber wenn man 3 macht, gibt es mehr Leute, die 4 fordern und verstehen.
Die Grenze dieses Ansatzes ist allerdings: Wenn man den Anfang oder das Ende eines Prozesses automatisieren kann, ist das okay; wenn es aber mitten im Prozess zwei isolierte Automatisierungsinseln gibt, ist es nicht besser als ein Kommandozeilen-Tool mit Prompts.
Man beschreibt, was zu tun ist, und stellt Boilerplate, Beispiele und parametrisierte ausführbare Dateien bereit.
Notebooks faktisch als Dokumentationssystem zu verwenden, wäre ziemlich schön. Allerdings sind sie zu schwergewichtig, um sie als Schicht auf eine Cloud-SaaS wie Confluence zu legen, und in ihrer aktuellen Form bieten sie auch zu viele Möglichkeiten für Probleme wie Privilege Escalation.
Dieser Ansatz verwandelt eine Confluence-Anleitungsseite in einen halb interaktiven Walkthrough. Das ist die bewusste Seite.
Die andere Seite ist Testautomatisierung. Am Anfang besteht sie aus einem Haufen überangepasster XPath-Ausdrücke und undokumentierten Schritten. Jedes Mal, wenn sich die Anwendung ändert, entdeckt man diese Schritte erneut. Das ist die unbewusste Seite.
Hoffentlich erreicht man den Gipfel und kann schnell ausführen, weiß aber immer noch, wie man dorthin gekommen ist und warum es so geworden ist.
Man kann den Benutzer einfach mit
Execute command (y/N)?bestätigen lassen.Von einem Terminal in ein anderes zu kopieren und einzufügen, hasse ich wirklich. Auch das frisst viel Konzentration.
Nur bei manuellen Aufgaben, die noch keine Befehle sind, sollte man den Benutzer fragen:
Look up the e-mail address for foo. Paste it here:oderPut that shit in 1Password: Are you done (y/N)?Ein interessanter Ansatz.
Aber die als Beispiel genannte Aufgabe zeigt nur, wie unsicher die Vergabe von SSH-Schlüsseln in diesem Unternehmen war. In der Praxis sollte der Benutzer selbst den privaten Schlüssel erzeugen und nur den öffentlichen Schlüssel an den Systemadministrator weitergeben, damit der Zugriff gewährt wird. Zu keinem Zeitpunkt sollte der Systemadministrator eine Kopie des privaten Schlüssels haben, nicht einmal vorübergehend. Deshalb sollte der 1Password-Schritt selbst gar nicht nötig sein.
Nebenbei: Ich bin der Autor von github-keygen, einem Tool, das die Erstellung eines SSH-Schlüssels speziell für GitHub-Zugriff und die SSH-Konfiguration für diesen Kontext automatisiert.
https://github.com/dolmen/github-keygen
Der Benutzer muss den privaten Schlüssel selbst erzeugen und nur den öffentlichen Schlüssel an den Systemadministrator weitergebenwar immer nervig umzusetzen.Wir sind bei SSH auf zertifikatsbasierte Authentifizierung umgestiegen und verschieben keine öffentlichen Schlüssel mehr hin und her. Der ganze Prozess ist wirklich einfacher geworden.
Erst etwa ein Jahr, nachdem dieser Artikel erschienen war, habe ich diesen Ansatz schließlich ausprobiert.
Wegen eines Bugs in unserer Toolchain war das Runbook für Hotfixes ungefähr doppelt so kompliziert wie der normale Release-Prozess.
Es bekam zwar nicht die Anerkennung, die es verdient hätte, aber etwas, das früher etwa alle zehn Wochen nur für Sev-1-Issues oder Epic-Arbeiten in der Abschlussphase genutzt wurde, kam im Schnitt einmal pro Woche zum Einsatz, in manchen Wochen sogar dreimal. Weil nicht mehr alles per Feature Toggle gebaut werden musste, konnten wir uns viel tiefer in die technischen Schulden hineinarbeiten.
Wenn man in einem kleinen Unternehmen Betriebsdaten leicht in eine Pre-Production-Umgebung replizieren kann, sieht man solche Ergebnisse vielleicht nicht. Aber wir hatten mehr als 150 Endpunkte, mit denen wir kommunizierten, im Schnitt wohl etwa drei pro Service. Es gab sehr viele Datensätze, und einige davon wurden auf eine Art gesammelt, die wie Kafka vor Kafka war.
Es gab nur eine Person, die versuchte, Betriebsdaten zu replizieren, und auch dieser Person fehlten Zeit und Energie, sodass es nur ein- oder zweimal im Jahr passierte. Dieses Tempo war viel langsamer als die Geschwindigkeit, mit der sich Kunden und Funktionen änderten. Am Ende mussten wir am Blue-Green-Deployment-Prozess und an jmeter herumprobieren, um herauszufinden, ob wir nahe genug dran waren und wie wir vor dem Livegang Erfolg/Misserfolg messen sollten.
Am Ende war es der kleinteilige und fehleranfällige Build-Prozess, der die Leute ausbremste, und erst nachdem ich ihn halb automatisiert hatte, löste sich das.
Später, als die Nutzung zunahm, suchte ich alle URLs aus den manuellen Schritten heraus und packte sie in eine Lookup-Tabelle im Tool; außerdem wurden sie auch im normalen Verifizierungsprozess für Release-Freigaben angezeigt. Dadurch wurde es für den Koordinator etwas schneller und weniger stressig. Der Prozess war so nervig, dass drei Teams ihn im Wechsel übernahmen, um die Belastung zu teilen.
Bedeutet „die Aktivierungsenergie für die Automatisierung von Aufgaben senken“ letztlich, dass ein Skript, das nichts tut, später Schritte bekommt, die tatsächlich etwas automatisieren?
Als Platzhalter für künftige Automatisierung betrachtet fühlt sich das wie ein guter Kompromiss zwischen Automatisierung und Effizienz an. Man kann einen ersten Versuch starten, ohne zu viel zu investieren, und sich niedrig hängende Früchte für später aufheben, wenn der Aufwand klarer wertvoll wird.
Da jeder Schritt des Ablaufs jetzt in eine Funktion gekapselt ist, kann man den Text eines bestimmten Schritts durch Code ersetzen, der die eigentliche Aktion automatisch ausführt.
class Foo(object): def run(self, context): ...Objekte mit nur einer Ausführungsmethode sind in Python bereits eingebaut. Sie heißen Funktionen.
def foo(context): ...Beim Funktionsansatz fügt man eine neue Funktion auf globaler Ebene hinzu, wenn man sie braucht. Bei ein oder zwei ist das völlig in Ordnung, aber wenn es mehr werden, stehen auf derselben Ebene jede Menge Funktionen herum, und die Abhängigkeiten zwischen ihnen sind nicht mehr klar erkennbar.
In einem davon geht es um Beispiele, wie man unnötig abstrahierten Code vereinfacht.
Großartig, aber nicht unterbrechbar.
Es wäre schön, vorab alle Schritte zu zeigen und dann beim Fortschritt die einzelnen Punkte abzuhaken. Manchmal ist es gut, sich tatsächlich aus einer breiteren Perspektive vorzubereiten.
Man könnte eine Zusammenfassung auch als Log in eine Datei schreiben.
Es gibt so vieles, was man verbessern könnte, dass am Ende vielleicht gerade die einfachste Lösung die beste ist.
Allerdings ist es so leicht, mit einem Shell-Skript, das nichts tut, anzufangen, dass es schwer ist, es nicht bis zum Ende zu bauen. Diese Mühe könnte man vielleicht besser darauf verwenden, einen einzelnen Schritt zu automatisieren. Man kann leicht in einen interessanten, aber nicht besonders produktiven Sumpf geraten, etwa welche TUI-Bibliothek man nehmen und wie man die Struktur aufbauen soll.
Jeder Schritt ist eine Regel mit einem Namen wie
*.done, und wenn er abgeschlossen ist, wird eine.done-Datei erzeugt. Man kann jederzeit abbrechen, das Skript ändern, um etwas zu reparieren, und dann mitmakefortsetzen.Aber dieses Makefile zu schreiben ist wirklich schmerzhaft. Gibt es eine bessere Lösung?
./do-the-thing.sh 2025aus und lege ein Verzeichnis 2025 an, in dem der Fortschritt gespeichert wird.Wenn der erste Schritt bestätigt ist, kann man die Datei
2025/first-steppertouchanlegen. Wenn das Skript abstürzt oder unterbrochen wird und danach erneut läuft, prüft es diese Datei und überspringt den ersten Schritt.Wenn sich etwas ändert und die Automatisierung nicht mehr funktioniert, ist es gut, ohne Zustandsverlust aussteigen, das Skript reparieren und erneut starten zu können.
Normalerweise lasse ich das Skript nur den nächsten manuellen Schritt ausgeben und dann beenden. So kann man das Terminal für andere Dinge verwenden. Über die Shell-History lässt sich das Skript leicht erneut ausführen.
Es gibt auch frühere Diskussionen. Viele Kommentare.
https://news.ycombinator.com/item?id=29083367 – vor 3 Jahren, 230 Kommentare
https://news.ycombinator.com/item?id=20495739 – vor 6 Jahren, 124 Kommentare
Ich kann kaum übertreiben, wie sehr ich diesen Ansatz mag.
Ich habe ihn in mehreren Projekten erfolgreich eingesetzt. Mein Lieblingsbeispiel ist ein 30-Millionen-Dollar-Operationsroboter, der wegen „menschlicher Faktoren“ bei Labortests scheiterte.
Ich bin in einem anderen Bereich tätig, in der Rechtspraxis, aber ich würde gern darüber nachdenken, wie wir diesen Ansatz in unserer Firma anwenden könnten.
Dieser Ansatz gefällt mir. Auch bei Systemen auf Basis von Programmiersprachen ab einer gewissen Komplexität mache ich bereits gern etwas Ähnliches. In der funktionalen Programmierung nennt man das wohl Löcher (holes).
not implemented-Fehler in Interfaces folgen einer ähnlichen Logik, aber ich halte es für ziemlich wertvoll, für mehrere voneinander abhängige Teile jeweils etwas Triviales zu schreiben, das zwar semantisch nichts leistet, aber gültige Ausgaben erzeugt. Dadurch geht das Erstellen dieser Teile schneller, und die Wahrscheinlichkeit steigt deutlich, dass man sie einzeln testet und baut. Man muss weniger oft mehrere Teile auf einmal schreiben, bevor man überhaupt mit dem Testen anfangen kann.Im Skript-Kontext, wie in dem im Artikel behandelten Anwendungsfall, kann es allerdings schwer sein, dass diese Gültigkeit auf Typebene wichtig wird. Denn die Effekte auf der Kommandozeile wären aus funktionaler Sicht größtenteils wohl Seiteneffekte. Weil das Ganze aber sequenziell abläuft, ist die Auswirkung viel geringer, und dank der Warte-Prompts kann man die manuellen Schritte, die man ohne Skript ohnehin machen müsste, mit geringem Aufwand beibehalten und zugleich die Reihenfolge der Arbeitsschritte bewahren.
Das Gerüst ist unvollständig, aber grundsätzlich nützlich.
Theoretisch klingt das gut, praktisch dürfte es aber schwierig sein.
Wenn ein Betriebsteam dieselben Aufgaben immer wieder ausführt und sieht, dass ein Skript, das nichts tut, wirklich nichts tut, wird es das schnell nicht mehr verwenden, sobald es glaubt, die Schritte auswendig zu kennen, oder sobald die manuelle Ausführung schneller oder interessanter erscheint.
Ich habe viel Automatisierung und Dokumentation für Betriebsteams geschrieben, aber die Leute dazu zu bringen, sie zu nutzen und dauerhaft zu nutzen, war immer ein Problem. Auch Änderungen an der Dokumentation mussten angekündigt werden. Sobald Menschen glauben, etwas zu können, hören sie sehr schnell auf, die Doku zu lesen.
In einer perfekten Welt wäre dieser Ansatz sehr sinnvoll, und für persönliche Aufgaben könnte ich ihn mir auch vorstellen. Aber die Realität ist selten perfekt. Ich würde diese Methode wohl nur dann einsetzen, wenn bereits 90 % automatisiert sind und nur noch ein ungelöster Schritt übrig ist. Selbst dann könnten Teile des Betriebsteams diesen manuellen Schritt überspringen und annehmen, das Ganze sei vollständig automatisierte Magie.