- Eine als Open-Source-Dokument veröffentlichte Referenz, die Prinzipien für das Design von CLI-Programmen und konkrete Richtlinien als moderne Neuinterpretation der traditionellen UNIX-Philosophie zusammenfasst und sich vor allem an Entwickler richtet, die Kommandozeilen-Tools bauen
- CLI hat sich von einer reinen Skripting-Plattform zu einer menschenzentrierten textbasierten UI weiterentwickelt, und entsprechend müssen auch die Designprinzipien aktualisiert werden
- Komponierbarkeit (composability) und Benutzerfreundlichkeit stehen nicht im Widerspruch; wenn man UNIX-Konventionen wie Standard-Ein/Ausgabe, Pipes und Exit-Codes einhält, lassen sich beide zugleich erreichen
- Bietet konkrete Empfehlungen auch für in der Praxis oft übersehene Details wie Hilfetexte, Fehlermeldungen, Ausgabeformate, Interaktivität und Konfigurationssysteme
- Die Zukunftskompatibilität und das Vertrauen der Nutzer von CLI-Tools hängen von der Stabilität der Schnittstelle und von Transparenz bei Analytics-Daten ab; dieser Leitfaden definiert dafür eine Basislinie
Philosophie (Philosophy)
Menschenzentriertes Design
- Traditionelle UNIX-Befehle wurden meist mit der Annahme entworfen, dass sie vor allem von anderen Programmen verwendet werden; heute werden CLIs jedoch überwiegend direkt von Menschen benutzt, daher ist Human-First-Design nötig
- Früher war CLI „machine-first“, heute ist es zu einer „human-first“ textbasierten UI geworden
Kombinierbare kleine Bausteine
- Der Kern der UNIX-Philosophie ist, kleine, einfache Programme zu kombinieren, um größere Systeme zu bauen, und das gilt bis heute
- Standard-
stdin/stdout/stderr, Signale und Exit-Codes sichern die Verbindung zwischen Programmen; JSON unterstützt zusätzlich strukturierteren Datenaustausch - Software wird zwangsläufig Teil eines größeren Systems, und ob sie darin ein gut funktionierender Baustein ist, entscheidet sich bereits im Design
Konsistenz
- Terminal-Nutzer sind mit bestehenden Konventionen vertraut, daher wird empfohlen, dass CLI-Tools bestehenden Mustern folgen
- Wenn Konsistenz jedoch die Benutzerfreundlichkeit verschlechtert, kann man Konventionen mit Bedacht brechen
Angemessene Informationsmenge
- Wenn ein Befehl mehrere Minuten ohne jede Ausgabe wartet, ist das „zu wenig“ Information; wenn er massenhaft Debug-Logs ausgibt, ist das „zu viel“ Information
- Ein gutes Gleichgewicht bei der Informationsmenge ist entscheidend dafür, dass Software Nutzer wirksam unterstützt
Auffindbarkeit (Ease of Discovery)
- GUI zeigt alle Funktionen sichtbar auf dem Bildschirm, während CLI oft fälschlich als rein gedächtnisabhängig angesehen wird
- Mit umfassenden Hilfetexten, guten Beispielen und Vorschlägen für den nächsten Befehl kann auch eine CLI leicht erlernbar gestaltet werden, indem man Techniken aus GUIs übernimmt
CLI als Gespräch
- Die Nutzung einer CLI hat durch wiederholtes Ausprobieren und Scheitern eine dialogartige Struktur; Vorschläge zur Fehlerkorrektur, Anzeigen von Zwischenständen und Bestätigungen vor riskanten Aktionen sind Designtechniken, die das nutzen
- Die schlechteste Interaktion ist ein feindseliger Dialog, der Nutzer hilflos macht; die beste ist ein angenehmer Austausch, der ein Gefühl von Erfolg vermittelt
Robustheit (Robustness)
- Software muss tatsächlich robust sein und sich auch robust anfühlen
- Zentrale Punkte sind der elegante Umgang mit unerwarteten Eingaben, die Wahrung von Idempotenz, Hinweise zum Fortschritt und Zurückhaltung beim Anzeigen von Stack-Traces
- Wer komplexe Sonderfälle reduziert und Dinge einfach hält, erhöht die Robustheit
Empathie (Empathy)
- CLI-Tools sind kreative Werkzeuge für Entwickler und sollten Freude bei der Nutzung machen
- Das Design sollte so durchdacht sein, dass Nutzer spüren, dass das Tool auf ihrer Seite ist
Chaos
- Die Terminal-Welt ist voller Inkonsistenzen, aber genau dieses Chaos ist auch eine Quelle freier Kreativität
- „Wenn ein Standard der Produktivität oder der Benutzerzufriedenheit eindeutig schadet, wirf ihn weg.“ — Jef Raskin
Richtlinien — Die Grundlagen (The Basics)
- Eine Library zum Parsen von Argumenten verwenden: empfohlene Libraries je Sprache sind unter anderem Go(Cobra, cli), Python(Click, Typer, Argparse), Rust(clap) und Node(oclif)
- Bei Erfolg Exit-Code 0, bei Fehlschlag ein von 0 verschiedener Code zurückgeben — daran erkennen Skripte Erfolg oder Fehler
- Standardausgabe an
stdout, Meldungen wie Logs und Fehler anstderrsenden
Richtlinien — Hilfe (Help)
- Bei den Flags
-hoder--helpausführlichen Hilfetext anzeigen; das gilt ebenso für Subcommands - Bei Ausführung ohne Argumente kompakte Hilfe anzeigen (mit Beschreibung, 1–2 Beispielen, Flag-Erklärungen und Hinweis auf
--help)jqwird als Beispiel für eine gute Umsetzung genannt
- Verschiedene Formen von Hilfeanfragen wie
--help,-hoderhelp subcommandallesamt unterstützen - Oben im Hilfetext Link zur Web-Dokumentation und Feedback-Kanal bereitstellen
- Zuerst Beispiele zeigen — empfohlen wird ein Aufbau als Geschichte, die schrittweise zu komplexeren Anwendungsfällen führt
- Häufig verwendete Flags und Befehle oben im Hilfetext platzieren (vgl. die Struktur von
git) - Mit Formatierung wie fetten Überschriften die Scanbarkeit verbessern, dabei aber terminalunabhängige Methoden verwenden
- Wenn Nutzer etwas falsch eingeben, kann das Tool die Absicht erraten und Korrekturen vorschlagen — automatische Ausführung sollte aber mit Vorsicht entschieden werden
- Falsche Eingaben können logische Fehler und nicht bloß Tippfehler sein; außerdem entsteht bei automatischer Korrektur die Last, diese Syntax dauerhaft zu unterstützen
Richtlinien — Dokumentation (Documentation)
- Webbasierte Dokumentation bereitstellen — unverzichtbar für Durchsuchbarkeit und das Teilen von Links
- Terminalbasierte Dokumentation bereitstellen — bleibt mit der installierten Version synchron und ist auch offline zugänglich
- man-Seiten in Betracht ziehen — sie können etwa mit Tools wie
ronnerzeugt werden; empfohlen wird, den Zugriff auch per Subcommand zu ermöglichen, wie beinpm help ls
Richtlinien — Ausgabe (Output)
- Menschliche Lesbarkeit zuerst — ob an Menschen ausgegeben wird, lässt sich über den TTY-Status erkennen
- Text-Streams sind die universelle Schnittstelle von UNIX; daher sollte auch maschinenlesbare Ausgabe unterstützt werden
- Wenn menschenfreundliche Ausgabe die Pipe-Kompatibilität beeinträchtigt, mit dem Flag
--plainschlichte Textausgabe anbieten - Bei Übergabe des Flags
--jsonAusgabe im JSON-Format unterstützen - Ausgabe bei Erfolg knapp halten, wenn nicht nötig auch ganz weglassen — für Skripte Unterstützung zur Unterdrückung per Option
-qanbieten - Nutzer über Statusänderungen informieren — ein gutes Beispiel ist, dass
git pushden Status des Remote-Branches ausgibt - Die Ausgabe so gestalten, dass man wie bei
git statusden aktuellen Systemzustand leicht prüfen und zugleich die nächsten Schritte erkennen kann - Farben gezielt einsetzen und sie bei Pipes,
NO_COLOR,TERM=dumb,--no-colorund ähnlichen Bedingungen zwingend deaktivieren - In Nicht-TTY-Umgebungen keine Animationen oder Spinner anzeigen (um CI-Logs nicht zu verschmutzen)
- Emojis und Symbole nur verwenden, wenn sie die Verständlichkeit erhöhen (
yubikey-agentwird als Beispiel genannt) - Informationen, die nur Entwickler verstehen, aus der Standardausgabe heraushalten und nur im Verbose-Modus zeigen
stderrnicht wie eine Log-Datei verwenden — standardmäßig möglichst keine Log-Level-Labels wieERRoderWARNausgeben- Bei sehr großen Ausgaben den Einsatz eines Pagers wie
lesserwägen — nur in TTY-Umgebungen aktivieren, empfohlen sind die Optionenless -FIRX
Richtlinien — Fehler (Errors)
- Erwartbare Fehler in für Menschen verständliche Meldungen umschreiben (z. B. „
chmod +w file.txtausführen erforderlich“) - Ein gutes Signal-Rausch-Verhältnis wahren — Fehler desselben Typs unter einer gemeinsamen Überschrift bündeln
- Wichtige Informationen ans Ende der Ausgabe setzen — roten Text gezielt und sparsam einsetzen
- Bei unerwarteten Fehlern Debug-Informationen und Anleitung zum Einreichen eines Bug-Reports mit ausgeben
- Die URL für Bug-Reports so gestalten, dass Informationen automatisch vorausgefüllt werden und das Einreichen leichter fällt
Richtlinien — Argumente und Flags (Arguments and Flags)
- Argumente (
args) sind positionsbasiert, Flags sind namensbasiert — Flags gegenüber Argumenten bevorzugen - Für alle Flags auch eine Langform anbieten (z. B.
-hund--helpgleichzeitig unterstützen) - Einzelzeichen-Flags nur für häufig genutzte Flags verwenden
- Wenn es Standards gibt, standardisierte Flag-Namen verwenden (
-f/--force,-q/--quiet,-v,--jsonusw.) - Standardwerte so setzen, dass sie für die meisten Nutzer geeignet sind
- Wenn Argumente oder Flags fehlen, Eingabe per Prompt anfordern, jedoch in nicht-interaktiven Umgebungen niemals erzwingen
- Vor riskanten Aktionen Bestätigung anfordern — je nach Risikostufe per
y/n, per Dry-Run oder durch direkte Texteingabe- Unterschieden wird zwischen mild (Datei löschen), moderate (Verzeichnis löschen, Remote-Ressourcen ändern) und severe (gesamten Server löschen)
- Bei Datei-Ein/Ausgabe mit
-das Lesen/Schreiben überstdin/stdoutunterstützen (z. B.curl ... | tar xvf -) - Über Flags keine Secrets direkt entgegennehmen — empfohlen werden etwa ein Flag wie
--password-fileoder die Nutzung vonstdin(wegen Sichtbarkeit inps-Ausgaben und Shell-Historie)
Richtlinien — Interaktivität (Interactivity)
- Prompts und interaktive Elemente nur anzeigen, wenn
stdinein TTY ist - Wenn
--no-inputübergeben wird, alle Prompts deaktivieren - Bei Passworteingaben Echo deaktivieren (eingegebener Inhalt wird nicht auf dem Bildschirm angezeigt)
- Klar darauf hinweisen, dass Nutzer jederzeit abbrechen können —
Ctrl-Cmuss immer funktionieren
Richtlinien — Subcommands (Subcommands)
- Zwischen Subcommands Konsistenz bei Flag-Namen und Ausgabeformaten wahren
- Komplexe Tools sollten eine zweistufige Subcommand-Struktur im Format
noun verboderverb nounverwenden (z. B.docker container create) - Mehrdeutige oder sehr ähnliche Subcommand-Namen vermeiden (z. B.
updateundupgradenicht parallel verwenden)
Richtlinien — Robustheit (Robustness Guidelines)
- Eingaben früh validieren und bei fehlerhaften Daten mit einer leicht verständlichen Fehlermeldung frühzeitig beenden
- Reaktionsfähigkeit ist wichtiger als Geschwindigkeit — innerhalb von 100 ms irgendetwas ausgeben
- Für lange laufende Aufgaben eine Fortschrittsanzeige (progress bar) bereitstellen — mögliche Libraries sind Python(
tqdm), Go(schollz/progressbar) und Node(node-progress) - Bei Parallelverarbeitung darauf achten, dass Ausgaben nicht durcheinandergeraten
- Netzwerk-Timeouts konfigurieren — inklusive Standardwerten, um endloses Warten zu verhindern
- Nach temporären Fehlern ein Fortsetzen vom vorherigen Zustand bei Wiederholung ermöglichen
- Crash-only-Design — eine Struktur wählen, die ohne Aufräumarbeiten sofort beendet werden kann, um Idempotenz zu sichern
Richtlinien — Zukunftskompatibilität (Future-proofing)
- Änderungen abwärtskompatibel additiv halten
- Vor Breaking Changes vorab im Programm warnen
- Änderungen an menschenlesbarer Ausgabe sind im Allgemeinen zulässig — für Skripte sollte zur Nutzung von
--plainoder--jsongeraten werden - Keine Catch-all-Subcommands — sonst kann später kein gleichnamiges echtes Subcommand mehr hinzugefügt werden
- Keine automatische Zulassung von Subcommand-Abkürzungen — nur explizite Aliase erlauben und diese stabil halten
- Keine „Zeitbomben“ — externe Abhängigkeiten minimieren, damit das Tool auch in 20 Jahren noch funktionieren kann
Richtlinien — Signale und Steuerzeichen (Signals)
- Bei Empfang von
Ctrl-C(INT-Signal) sofort beenden, für Aufräumarbeiten aber ein Timeout setzen - Während der Aufräumphase darauf hinweisen, dass ein erneutes
Ctrl-Cein erzwungenes Beenden auslösen kann (siehe Beispiel Docker Compose) - Programme so entwerfen, dass sie auch starten können, wenn Aufräumarbeiten noch nicht abgeschlossen sind
Richtlinien — Konfiguration (Configuration)
Priorität bei der Anwendung von Konfiguration (hoch → niedrig):
- Flags → aktuelle Shell-Umgebungsvariablen → Konfiguration auf Projektebene (
.env) → Konfiguration auf Benutzerebene → systemweite Konfiguration
Empfehlungen nach Konfigurationstyp:
-
Einstellungen, die sich bei jedem Aufruf ändern (Debug-Level, Dry-Run): Flags verwenden
-
Einstellungen, die je nach Projekt oder Rechner variieren (Pfade, Farben, HTTP-Proxy): Kombination aus Flags und Umgebungsvariablen
-
Projektweit geteilte Einstellungen (Typ Makefile,
package.json): versionierte Dateien verwenden -
Die XDG Base Directory Spec einhalten — Konfigurationspfade auf Basis von
~/.configwerden empfohlen (unterstützt etwa von yarn, fish, neovim, tmux) -
Beim automatischen Ändern von Konfigurationsdateien anderer Programme unbedingt die Zustimmung des Nutzers einholen
Richtlinien — Umgebungsvariablen (Environment Variables)
- Umgebungsvariablen eignen sich für Verhalten, das sich je nach Ausführungskontext ändert
- Für Namen nur Großbuchstaben, Ziffern und Unterstriche verwenden; sie dürfen nicht mit einer Ziffer beginnen
- Einzeilige Werte werden empfohlen — mehrzeilige Werte führen zu Kompatibilitätsproblemen mit dem Befehl
env - Zuerst gängige Umgebungsvariablen wie
NO_COLOR,DEBUG,EDITOR,HTTP_PROXY,SHELL,TMPDIR,HOME,PAGERprüfen - Unterstützung zum Einlesen projektspezifischer
.env-Dateien wird empfohlen —.envist jedoch kein Ersatz für eine formale Konfigurationsdatei- Grenzen von
.env: nicht versioniert, keine Historie, nur der Typ String, anfällig für Encoding-Probleme
- Grenzen von
- Keine Secrets aus Umgebungsvariablen lesen — sie werden an alle Prozesse vererbt, können in Logs auslaufen und über
docker inspectodersystemctl showoffengelegt werden- Secrets sollten nur über Credential-Dateien, Pipes,
AF_UNIX-Sockets oder Secret-Management-Services entgegengenommen werden
- Secrets sollten nur über Credential-Dateien, Pipes,
Richtlinien — Benennung (Naming)
- Einfache und gut merkbare Wörter verwenden — sind sie zu allgemein, drohen Konflikte mit anderen Befehlen
- Nur Kleinbuchstaben und bei Bedarf Bindestriche verwenden (
curlist ein gutes Beispiel,DownloadURLein schlechtes) - Kurz halten, aber extrem kurze Namen wie
cd,ls,psfür allgemeine Utilities reservieren - Das Umbenennungsbeispiel des Vorläufers von Docker Compose von
plumzufigzudocker composezeigt konkret, dass Tippkomfort ein wichtiges Kriterium bei der Benennung ist
Richtlinien — Distribution (Distribution)
- Wenn möglich als einzelne Binärdatei verteilen — z. B. mit PyInstaller
- Falls eine einzelne Binärdatei nicht möglich ist, plattformnative Paketinstaller verwenden
- Deinstallationsmethode am Ende der Installationsanleitung angeben
Richtlinien — Analytics
- Keine Nutzungs- oder Crash-Daten ohne Zustimmung des Nutzers übertragen
- Falls Daten erhoben werden, klar offenlegen, was gesammelt wird, warum, wie anonymisiert wird und wie lange die Daten gespeichert werden
- Standardmäßig wird Opt-in empfohlen — bei einem Opt-out-Modell muss dies beim ersten Start oder auf der Website klar mitgeteilt werden
- Drei Beispiele werden vorgestellt: Angular.js (explizites Opt-in), Homebrew (Google Analytics, FAQ offengelegt), Next.js (anonyme Telemetrie standardmäßig aktiviert)
- Als Alternativen zu Analytics kommen Messung der Web-Dokumentation, Zählung von Downloads und direkte Nutzerinterviews in Frage
1 Kommentare
Hacker-News-Kommentare
stdoutsollten grundsätzlich keine Animationen angezeigt werden.stderrist für Logging, Hinweise usw. gedacht, undstdoutsollte unabhängig davon, ob es sich um ein TTY handelt, nützliche Ausgaben liefern.