1 Punkte von GN⁺ 2026-02-02 | 1 Kommentare | Auf WhatsApp teilen
  • Ein Beispiel aus der Entwicklung einer Anwendung zur Berichtserstellung, bei der eine --dry-run-Option hinzugefügt wurde, um das Ausführungsergebnis zu simulieren
  • Diese Option gibt aus, welche Aktionen ausgeführt würden, ohne tatsächliche Änderungen vorzunehmen, und ermöglicht so sichere Tests und schnelles Feedback
  • Entwickler können damit Konfiguration, Zugänglichkeit und Systemzustand sofort prüfen und sie als alltägliches Verifizierungswerkzeug nutzen
  • Als Nachteil wird eine leicht erhöhte Komplexität genannt, da im Code das Flag dryRun geprüft werden muss
  • Bei imperativen Anwendungen ist die --dry-run-Funktion sehr nützlich; je früher sie im Projekt eingeführt wird, desto höher ist die Entwicklungseffizienz

Hintergrund

  • Die neu entwickelte Anwendung zur Berichtserstellung erzeugt an jedem Werktag Berichte, komprimiert sie, lädt sie auf einen sftp-Server hoch, prüft Fehlerantworten und verschickt Benachrichtigungs-E-Mails
    • In jedem Schritt erzeugte Dateien und Feedback-Dateien werden in unterschiedliche Verzeichnisse pro Phase verschoben
  • Zu Beginn der Entwicklung erinnerte man sich an Subversion und die --dry-run-Option vieler Linux-Befehle und fügte dieselbe Funktion hinzu
    • Diese Option gibt aus, was bei der Ausführung passieren würde, ohne echte Änderungen vorzunehmen
  • Bei einer Ausführung mit --dry-run werden die zu erzeugenden und auszuschließenden Berichte, die zu komprimierenden und zu verschiebenden Dateien sowie die per sftp hoch- und herunterzuladenden Dateien schrittweise ausgegeben

Nutzung und Vorteile

  • Eine so nützliche Funktion, dass sie im Entwicklungs- und Testprozess täglich verwendet wird
  • Wenn sie zur Zustandsprüfung vor der Ausführung verwendet wird, lassen sich Konfiguration, Zugänglichkeit und Systemzustand sofort überprüfen
    • Da keine tatsächlichen Änderungen vorgenommen werden, kann sie sicher ausgeführt werden
  • Wenn das Datum der Statusdatei eines Berichts geändert wird, lässt sich sofort prüfen, ob dieser Bericht erzeugt werden würde
    • Die eigentliche Berichtserstellung braucht Zeit, aber --dry-run liefert schnelles Feedback
  • Auch beim Testen des Gesamtsystems bietet sie eine schnelle Validierungsschleife

Nachteile

  • Da im Code das Flag dryRun wiederholt geprüft werden muss, entsteht eine leichte Verunreinigung des Codes
    • In jedem Hauptschritt wird das Flag geprüft, sodass statt der echten Ausführung nur Logs ausgegeben werden
  • Diese Prüfungen reichen jedoch nicht tief hinein, und innerhalb der Logik zur Berichtserstellung ist keine gesonderte Behandlung nötig
    • Geprüft wird nur auf der übergeordneten Ebene, die über die Ausführung entscheidet

Fazit

  • Anwendungen, die imperativ ausgeführt werden und Ergebnisse erzeugen, passen gut zu einer --dry-run-Option
    • Für reaktive Anwendungen, die auf Nachrichten warten, ist sie dagegen nicht geeignet
  • Dass sie früh im Projekt hinzugefügt wurde, half erheblich dabei, die Entwicklungseffizienz zu steigern
  • Sie ist nicht in jeder Situation nötig, wird aber im passenden Fall als sehr nützliches Werkzeug bewertet

1 Kommentare

 
GN⁺ 2026-02-02
Hacker-News-Kommentare
  • Bei der Interaktion mit zustandsbehafteten Systemen kann es auch bei --dry-run zu Race Conditions kommen
    Das Tool zeigt zwar, „was es tun würde“, aber zum Zeitpunkt der tatsächlichen Ausführung kann sich die Lage bereits geändert haben
    Deshalb bevorzuge ich den Ansatz des „plan“-Modus von Terraform. Dieser Modus erzeugt einen ausführbaren Plan, und wenn sich die Annahmen vom Planungszeitpunkt ändern, kann man abbrechen oder ein Rollback ausführen
    Außerdem muss man nicht überall im Code if dry_run: einstreuen und kann Planung und Ausführung trennen und zu etwas wie execute(plan()) vereinfachen

    • Der frühere AWS-DNS-Ausfall war ein ähnlicher Fall einer Race Condition
      Wegen eines Timing-Problems zwischen DNS Planner und Enactor hat ein veralteter Plan den aktuellen Plan überschrieben
      Das wurde auch im früheren HN-Thread behandelt
    • Damit implementiert man am Ende im Grunde einen Compiler (Spezifikation → Plan) und eine virtuelle Maschine (Plan → Ausführung)
    • Für Infrastruktur-Tools wie Terraform oder Ansible ist das ideal, für einfache Reporting-Tools kann es aber zu übermäßiger Komplexität führen
      Denn um einen Plan-Modus zu bauen, braucht man eine domänenspezifische Sprache oder passende Datenstrukturen
    • Ich baue auch ein Skript, das sensible Dateien verändert, und nutze dabei genau diesen Ansatz
      (1) Zustand des Dateisystems erfassen und den Plan speichern → (2) prüfen, ob sich der Zustand nicht verändert hat, dann ausführen und protokollieren → (3) mit dem vorherigen Zustand vergleichen und auf Datenverlust prüfen
      Ich nutze diese drei Schritte getrennt als eigene Skripte oder Flags
    • Dann frage ich mich, wie man so einen Plan-Modus wohl auf den Befehl rm anwenden könnte
  • Wenn ein Tool kein --dry-run hat, baue ich mir manchmal selbst etwas Vergleichbares
    Bevor ich zum Beispiel einen komplexen sed-Befehl ausführe, vergleiche ich mit diff vorab die Änderungen
    Mit diff -u <(echo "hello") <(echo "hello" | sed "s/hello/hi/g") kann man sich die Unterschiede ansehen
    Mehr dazu habe ich in meinem Blog gesammelt

  • Ich mag das Muster --dry-run, aber der Codepfad für den Dry Run muss sich genauso verhalten wie der echte Pfad
    Wenn man nur ausgibt, „was passieren würde“, und die eigentliche Logik überspringt, kann man Bugs in der echten Ausführung übersehen
    Bis kurz vor Datenbank-Schreibvorgängen oder API-Aufrufen sollte alles identisch laufen

    • Manche würden aber sagen, dass das Integrationstests und Dry Runs verwechselt
      Ein Dry Run zeigt, „was passieren wird“, während echtes Testen ein eigener Bereich ist
  • Ich bevorzuge eher --commit oder --execute, während die Standardausführung read-only (dry) bleibt
    So sinkt die Wahrscheinlichkeit, versehentlich echte Änderungen auszulösen

    • Ich nutze diesen Ansatz seit 8 Jahren, und er war sicher, weil Änderungen nur mit explizitem --commit passieren
      Dagegen gab es viele Unfälle, weil jemand vergessen hat, --dry-run anzugeben
    • Auch mein Tool zur Deduplizierung von Verzeichnissen folgt diesem Muster
      Standardmäßig vergleicht es nur, und erst mit --execute werden tatsächlich Hardlinks eingesetzt
    • Bei manchen Tools, die ich früher genutzt habe, musste man vor der echten Ausführung noch eine bestimmte Zeichenfolge eingeben
      Solche Bestätigungsschritte helfen effektiv gegen Fehler
    • Ich persönlich mag auch Flags wie --wet-run. Je nach Situation ist ein Flag mit umgekehrter Bedeutung manchmal intuitiver
    • Ein Skript, das ich kürzlich gebaut habe, ist standardmäßig read-only, und für echtes Löschen muss man DELETE-ACCOUNT direkt eingeben
      Bis heute habe ich noch nie versehentlich ein Konto gelöscht
  • Um Code-Verschmutzung zu vermeiden, sollte man Persistenz als injizierbare Strategie auslagern
    Es ist keine gute Idee, überall if dry_run: zu verteilen
    Sicherer ist es eher, die Produktivausführung explizit als --wet-run zu markieren

    • Gut ist es, das Verhalten der Anwendung explizit zu modellieren und zentral zu verarbeiten
      Dann muss nur an einer Stelle entschieden werden, ob es ein Dry Run ist — im Stil von „functional core, imperative shell“
    • Allerdings wäre es lästig, jedes Mal etwas wie rm --wet-run tempfile.tmp einzugeben
      Besser wäre echte Ausführung als Standard und stattdessen eine Option --undo, um den letzten Vorgang rückgängig zu machen
    • Der Name --wet-run gefällt mir zwar nicht, aber ich habe auch schon Systeme genutzt, bei denen standardmäßig dry-run aktiv war und Änderungen nur mit explizitem --no-dry-run möglich waren
      Bei Services wäre es ideal, je nach Laufzeitumgebung (dev/prod) automatisch einen sicheren Modus zu wählen
    • In solchen Fällen können Design Patterns helfen, die Struktur sauber zu halten
  • Im Artikel hieß es, man habe früh --dry-run ergänzt und sei überrascht gewesen, wie nützlich das war,
    tatsächlich werden solche Flags aber oft von AI-Coding-Agenten (z. B. Claude) automatisch vorgeschlagen
    Dass viele CLI-Tools heute ähnliche Muster haben, könnte auch an dieser agentengetriebenen Konvergenz im Code liegen

    • Allerdings erwähnt der Autor ausdrücklich, dass die Inspiration von Subversions --dry-run kam, daher ist das als Begründung durchaus überzeugend
  • Ich ergänze bei CLI-Utilities oft ein Flag --really, sodass standardmäßig nur read-only gearbeitet wird
    Damit verlange ich eine bewusste Bestätigung, um Fehler zu vermeiden

    • Früher hatte ich auch schon einmal einen Befehl mit --i-meant-that
      Das war ein Kommando zum Löschen entfernter Maschinen, das standardmäßig 10 Sekunden wartete und Gelegenheit zum Abbrechen gab
      Zum Glück wurde dieses Flag nie falsch verwendet
  • Eines der großartigen Dinge an PowerShell ist, dass man mit einer einzigen Zeile [CmdletBinding(SupportsShouldProcess)]
    automatisch die Dry-Run-Funktion -WhatIf bekommt. Das ist eine sehr praktische Funktion

    • Außerdem wird auch -Confirm aktiviert, und über die Funktion ShouldProcess kann man mit der Bestätigungsschwelle für den Benutzer interagieren. Wirklich ein tolles Design
  • In einer internen CLI, die ich betreue, setze ich if not dry_run: im Bereich der REST-API-Aufrufe ein
    So kann ich statt echter Aufrufe CURL-Befehle protokollieren, damit sichtbar ist, welche Requests rausgehen würden
    Wenn die Verknüpfungen zwischen APIs komplexer werden, wird die Simulation aber schwierig und deutlich komplizierter als ein simples if not dry_run:

    • Wenn man die Struktur so gestaltet, dass die echte Aktion möglichst nur an einer Stelle ausgeführt wird, verhindert das Code-Verschmutzung
      Ich betreue auch viele CLIs für Automatisierungs-Pipelines und verwende dieses Muster in fast allen Tools
    • Es gibt aber auch die Meinung, dass zu viel REST in Konsolen-Tools ineffizient ist
      Wichtiger sei es, zuerst gute lokale Tools zu bauen
  • Wenn das Flag --dry-run im ganzen Code verstreut ist, sollte man besser das State-Machine-Pattern anwenden, um die einzelnen Schritte klar zu trennen