


Ein Kommandozeilentool bauen, das keine Nutzerdaten verliert und keinen nicht freigegebenen Code ausführt
Wenn eine KI ein CLI-Tool mit echten Nutzern baut oder ändert. Sie hält Optionen und Konfiguration kompatibel, stoppt statt Arbeit zu zerstören, führt Projekt-Hooks nur nach Freigabe aus, protokolliert jeden Subprozess und zeigt Lokales vor Netzwerkdaten.
Diese KI-Regeln verwenden
## Konfigurationsdatei und Befehlsoptionen kompatibel halten, während das Tool wächst
Sobald ein Tool echte Nutzer hat, muss sauberes Design gegen Kompatibilität abgewogen werden. Ein Redesign allein ist noch kein Grund, den Nutzern die Basis zu entziehen.
### Jeden externen Breaking Change begründen
Eine Änderung an einer externen Schnittstelle braucht eine konkrete, für Nutzer spürbare Verbesserung – reines Aufräumen genügt nicht. Bevorzugen Sie einen Abkündigungshinweis mit Migrationspfad gegenüber einem stillen Bruch.
### Die geschützte Oberfläche benennen
Legen Sie fest, welche Schnittstellen als Vertrag gelten (das Format der Konfigurationsdatei, die Kommandozeilen-Flags und -Argumente) und welche sich frei ändern dürfen (interne APIs, Speicherorte von Logs, die für Menschen lesbare Formatierung der Ausgabe). Schützen Sie die erste Gruppe; halten Sie die zweite flexibel, damit Refactorings günstig bleiben.
### Das Support-Fenster begrenzen
Legen Sie eine minimal unterstützte Laufzeitumgebung oder Toolchain-Version fest und erhöhen Sie sie nach einem vorhersehbaren Wartungsrhythmus statt ad hoc.
## Für jedes Konzept ein Wort verwenden, in Doku, Hilfetext und Kommentaren
Wählen Sie pro Konzept einen einzigen Begriff und verwenden Sie ihn identisch in Dokumentation, Hilfetexten und Code-Kommentaren. Abweichender Wortgebrauch verwirrt schleichend sowohl Nutzer als auch Maintainer.
### Das Glossar schriftlich festhalten
Führen Sie eine kurze Liste, die die Bedeutung jedes Begriffs festlegt und die zu vermeidenden Beinahe-Synonyme benennt. Unterscheiden Sie ähnlich klingende Konzepte ausdrücklich, etwa den ursprünglichen Checkout gegenüber einem zweiten, oder das Ziel eines Vorgangs gegenüber dem Arbeitsbereich, in dem er läuft.
### Ein tragendes Wort niemals doppelt belegen
Wenn ein Wort bereits ein Konzept benennt, verwenden Sie es nicht für ein zweites – wählen Sie für das zweite ein anderes Wort, selbst wenn die Wiederverwendung in einem einzelnen Satz natürlich klingt.
## Den Hilfetext mit jeder Verhaltensänderung anpassen und per Test abgleichen
Jede Verhaltensänderung bringt ihre Dokumentationsänderung in derselben Änderung mit. Die anzuwendende Prüffrage: Beschreibt der Hilfetext noch, was der Code tatsächlich tut?
### Pro Fakt eine primäre Quelle behalten
Bearbeiten Sie bei generierter Dokumentation die maßgebliche Quelle (die im Code eingebettete Hilfe und die Befehlsdefinitionen) und erzeugen Sie die gerenderten Kopien neu; bearbeiten Sie eine generierte Datei niemals von Hand. Enthält ein Verzeichnis sowohl generierte Kopien als auch von Hand verfasste Seiten, klären Sie vor der Bearbeitung, welche Rolle eine Datei spielt.
### Synchronität durch einen Test erzwingen
Ein Dokumentations-Sync-Test, der die gerenderte Ausgabe mit der Quelle vergleicht, erkennt Abweichungen mechanisch. Das Bearbeiten von Hilfetexten verändert auch die gerenderten Snapshots – erzeugen und prüfen Sie diese daher in derselben Änderung neu, damit der Test grün bleibt.
## Mit einem Fehler stoppen, statt Nutzerdateien ohne Zustimmung zu zerstören
Behandeln Sie Nutzerdaten als unantastbar. Ein Befehl, der fehlschlägt, dabei aber die Arbeit bewahrt, ist besser als einer, der „erfolgreich“ ist, indem er sie still zerstört.
### Fehlschlag dem stillen Verlust vorziehen
Wenn ein Vorgang nicht versionierte Dateien, nicht committete Änderungen oder andere Nutzerdaten zerstören könnte, brechen Sie mit einem Fehler ab, statt fortzufahren.
### Für zerstörende Aktionen ausdrückliche Zustimmung verlangen
Eine zerstörende Aktion, etwa das erzwungene Entfernen von Daten oder ein Überschreiben, läuft nur, wenn der Nutzer ausdrücklich darum gebeten hat. Löschen oder überschreiben Sie niemals als Nebeneffekt eines nicht verwandten Vorgangs; machen Sie das Aufräumen zu einem eigenen, vom Nutzer gewählten Schritt.
### Die fehlschlagende Variante bevorzugen, die Lücke zwischen Prüfung und Nutzung beachten
Existieren zwei Formen eines Vorgangs, wählen Sie die, die bei einem Konflikt fehlschlägt, statt der, die sich erzwingt. Seien Sie vorsichtig, wenn zwischen einer Sicherheitsprüfung und der davon geschützten Aktion Zeit vergeht: Der Zustand kann sich in diesem Zeitfenster ändern, erzwingen Sie den Vorgang beim Aufräumen also nicht.
## Jeden Subprozess durch einen Wrapper leiten, der ihn protokolliert und seine Dauer misst
Leiten Sie jeden externen Befehl über einen einzigen Ausführungs-Wrapper, statt die Prozess-API direkt aufzurufen. Ein einziger Pfad liefert konsistentes Debug-Logging und Timing quasi gratis, dazu eine einzige Stelle, um das Verhalten zu ändern.
### Ein Wrapper, ein Emitter
Halten Sie die Trace-Grammatik in genau einer Komponente und lassen Sie nur sie die zugrunde liegenden Log-Aufrufe schreiben, sodass ein Subprozess entweder über diese Schranke protokolliert wird oder gar keinen Eintrag erzeugt.
### Neue Spawn-Stellen melden sich aktiv an
Manche Spawn-Formen passen nicht in den Wrapper und bauen den Trace-Eintrag von Hand: kurz vor dem Spawn starten, bei Erfolg abschließen, bei Fehler als fehlgeschlagen markieren. Machen Sie die Schranke zu einem Muss-genutzt-Wert, der eine Assertion auslöst, wenn er unaufgelöst verworfen wird, sodass ein vergessener Abschluss einen Test scheitern lässt, statt still unprotokolliert zu bleiben. Losgelöste Hintergrundprozesse und interaktive Hilfsprogramme sind die dokumentierten, nicht protokollierten Ausnahmen.
### Geerbte Discovery-Variablen bereinigen
Manche Umgebungsvariablen werden aufgelöst, bevor ein Kindprozess sein Arbeitsverzeichnis prüft, sodass ein geerbter Wert das von Ihnen gewählte Verzeichnis still überschreiben kann. Jede Spawn-Stelle, die einen Nutzerbefehl in ein vom Tool bestimmtes Verzeichnis verlegt, muss diese Variablen leeren; Kindprozesse, die im eigenen Kontext des Nutzers laufen, behalten den geerbten Kontext, absolutiert.
## Nach Exit-Codes und JSON-Ausgabe entscheiden, nicht nach Meldungstext
Bevorzugen Sie Exit-Codes, maschinenlesbaren Status und strukturierte Formate gegenüber dem Parsen menschenlesbarer Meldungen, die bei Änderungen von Sprache, Version und Formulierung brechen.
### Den strukturierten Kanal wählen
Lesen Sie Porcelain- oder JSON-Ausgaben sowie numerische Statusflags statt der menschenlesbaren Standardausgabe. Kodiert ein Befehl seine Bedeutung im Exit-Code, verzweigen Sie anhand des Codes, nicht anhand des Meldungstexts.
### Unvermeidbare Brüchigkeit dokumentieren
Existiert keine strukturierte Alternative und müssen Sie Text parsen, vermerken Sie die Brüchigkeit direkt an der Parse-Stelle, damit die nächste Person weiß, dass es sich um ein bekanntes Risiko handelt und nicht um einen stabilen Vertrag.
## Zuerst lokale Daten zeigen und Netzwerkdaten nachträglich einstreamen
Reaktionsschnelligkeit ist ein Feature: Zeichnen Sie zuerst aus lokalen Daten, langsamere Details dürfen danach nachkommen.
### Das Netzwerk niemals das erste Bild blockieren lassen
Greifen Sie nur dann auf das Netzwerk zu, wenn die Anfrage des Nutzers es erfordert. Jeder Befehl sollte seine erste Ausgabe aus lokalen Daten rendern und aus dem Netzwerk stammende Details schrittweise nachliefern; ein Befehl, der sein erstes Bild erst zeichnen kann, wenn ein entfernter Aufruf zurückkehrt, ist der Fehlerfall – am schlimmsten bei einer frischen Installation. Ein synchroner Hot Path, etwa ein Shell-Prompt, darf das Netzwerk überhaupt nicht erreichen, auch nicht schrittweise.
### Für Live-Daten aus dem Netz nutzergebundene Kontexte bevorzugen
Streamen Sie die Ausgabe eines Befehls zeilenweise, statt sie zu puffern. Ein interaktiver Auswahldialog ist der nachsichtigste Ort für Netzwerkarbeit: Er zeichnet sofort, und handelt der Nutzer, bevor ein langsamer Aufruf zurückkehrt, wird die Anfrage einfach verworfen. Ein bis zum Abschluss durchlaufender Befehl kann erst enden, wenn jeder Aufruf zurückgekehrt ist – holen Sie Live-Daten aus dem Netz also schrittweise im Auswahldialog.
## Bei einem Interrupt alle Schleifen des laufenden Befehls abbrechen
Stirbt ein Kindprozess durch ein Signal (Interrupt oder Terminate), muss jede Schleife im Ausführungspfad im Vordergrund abbrechen, statt zur nächsten Iteration überzugehen.
### Den strukturierten Signalkanal auslesen
Erkennen Sie durch ein Signal ausgelöste Exits am strukturierten Signalfeld, niemals durch das Erschnüffeln eines Exit-Codes ab 128 oder das Parsen von Fehlertext. Geben Sie ein kanonisches Interrupted-Exit-Ergebnis weiter und brechen Sie die Schleife ab.
### Vor jedem fehlerverschluckenden Zweig prüfen
Führen Sie die Interrupt-Prüfung durch, bevor irgendeine Fehlerbehandlungsstrategie läuft, damit eine „warnen und weitermachen“-Richtlinie einen Interrupt nicht verschlucken kann. Jeder neue Code, der über Kindprozesse iteriert, führt diese Prüfung pro Durchlauf aus; sonst arbeitet ein einzelner Interrupt bei einem mehrstufigen Vorgang die verbleibenden Schritte einfach durch.
## Projektdefinierte Hooks und Aliasse nur nach Freigabe ausführen
Projektdefinierte Befehle (Lifecycle-Hooks, Aliase, aus der Projektkonfiguration gezogene Execute-Bodies) sind beliebiger Code, der in einem Repository mitgeliefert wird, das der Nutzer womöglich gerade erst geklont hat. Sie laufen erst, nachdem ein Freigabesystem sie genehmigt hat.
### Die Schranke ist die gesamte Sicherheitsgrenze
Bauen Sie niemals einen Codepfad, der projektdefinierte Befehle ohne diese Schranke ausführt. Eine Abkürzung nach dem Muster „wir haben den Vorgang bereits validiert, also führen wir auch dessen Hooks aus“ macht aus jedem Befehl, der die Projektkonfiguration berührt, eine Möglichkeit zur Remote-Code-Ausführung; die Schranke ist das Einzige, was zwischen dem Klonen eines Repositorys und einem Hook steht, der ein aus dem Internet gepipetes Skript ausführt.
### Die genehmigte Menge einmalig und unveränderlich festlegen
Überführen Sie die Befehlsmenge am Entscheidungspunkt in einen unveränderlichen, genehmigten Plan und lassen Sie den Ausführer nur diesen Wert konsumieren, ohne etwas zu behalten, woraus er neu ableiten könnte, sodass das erneute Auswählen eines nicht genehmigten Befehls zu einem Compile-Fehler wird statt zu einer Review-Prüfung. Ein Kontext, der nicht nachfragen kann, konsultiert den Freigabestatus nur lesend und führt ausschließlich die bereits genehmigte Teilmenge aus.
## Bei fehlgeschlagener Coverage-Prüfung nie ohne Zustimmung des Nutzers mergen
Mergen Sie niemals eine Änderung mit fehlgeschlagener Coverage-Schranke ohne ausdrückliche Zustimmung des Nutzers, selbst wenn die Plattform diese Schranke als „nicht erforderlich“ markiert – in der Praxis blockiert sie trotzdem.
### Wissen, wie das Signal gemeldet wird
Coverage-Tools melden Ergebnisse mitunter als Check-Runs statt als Commit-Status, ausgelesen über unterschiedliche APIs; eine Combined-Status-Abfrage kann einen Check-Run komplett übersehen, und das Ergebnis kann erst Minuten nach Abschluss des Jobs vorliegen. Fragen Sie die richtige API ab, bevor Sie eine Änderung als grün einstufen.
### Die Lücke schließen, nicht einfach durchwinken
Untersuchen Sie bei einem Coverage-Fehlschlag die Lücke und schließen Sie sie: Tests ergänzen oder ungenutzten Code löschen, einschließlich Sonderfall-Behandlungen, wenn ein Durchfallen auf den allgemeinen Pfad ausreicht. Halten Sie es für einen Fehlalarm, fragen Sie vor dem Merge den Nutzer, statt einseitig zu entscheiden. Führen Sie Coverage mit denselben Feature-Flags aus, die auch die CI-Pipeline verwendet, damit Sie einen Fehlschlag nicht damit abtun können, ein Feature sei deaktiviert gewesen.
## Vorhandene Bibliotheken nutzen und Warnungen beheben statt unterdrücken
Prüfen Sie vorhandene Abhängigkeiten, bevor Sie ein Hilfsprogramm selbst bauen. Eine bewährte Bibliothek für Pfadnormalisierung, Shell-Escaping, Einfärbung oder das Erkennen von Template-Variablen schlägt eine ad hoc gebaute Neuimplementierung, die Randfälle falsch behandelt.
### Warnungen nicht zukleistern
Unterdrücken Sie eine Warnung niemals pauschal, um sie zum Schweigen zu bringen. Löschen Sie toten Code, oder hinterlassen Sie einen datierten, erklärten Vermerk, der ihn mit konkret anstehender Arbeit verknüpft.
### Komplexe Systeme direkt vor Ort dokumentieren
Ein nicht offensichtliches System (eine Zustandsmaschine, zwischengespeicherter Zustand, modulübergreifende Koordination, subtile Invalidierung) trägt eine Spezifikation auf Modulebene, die Zweck, Kernentscheidungen, Verträge und Invarianten abdeckt; halten Sie diese aktuell.
### Tests aus dem Bibliothekscode heraushalten
Fügen Sie Bibliothekstypen keine nur für Tests gedachten Komfortmethoden hinzu; Tests rufen die echte API auf oder definieren eigene Hilfsfunktionen. Bevorzugen Sie bei mehrzeiligen Literalen echte eingebettete Zeilenumbrüche gegenüber Zeilenfortsetzungs-Escapes, die Leerraum still entfernen.
## Fehlern Kontext mitgeben und sie nie in Abstürze verwandeln
Führen Sie bei jedem Fehler Kontext mit. Verwenden Sie explizite Business-Logic-Fehler für erwartete Fehlerzustände (ein unsauberer Arbeitsbereich, ein fehlender Branch, ein ungültiger Zustand) und kontextanreichernde Wrapper für Fehler bei I/O und externen Befehlen.
### Einen Fehlschlag niemals verschlucken
Verwenden Sie in einer Funktion, die einen Fehlertyp zurückgibt, niemals Panic-bei-Fehlen- oder Unwrap-artige Aufrufe, die aus einem behebbaren Fehlschlag einen Absturz machen; propagieren Sie ihn mit dem Fehler-Operator, lösen Sie einen expliziten Fehler aus, oder geben Sie einen zurück. Jeder umhüllte Fehler sollte der lesenden Person mitteilen, welcher Vorgang fehlgeschlagen ist und warum.
## Alte Konfigurationsschlüssel migrieren, statt sie still zu verwerfen
Halten Sie die gesamte Konfigurations-Kompatibilität in einer einzigen Schicht: Schreiben Sie veraltete Muster in die kanonische Form um, bevor der Parser sie zu sehen bekommt. Verwerfen Sie niemals still einen alten Konfigurationsschlüssel, denn das ist eine stille Verhaltensänderung; migrieren Sie ihn stattdessen.
### Ein Prädikat für Erkennung und Migration
Lassen Sie eine einzige idempotente Funktion sowohl ein veraltetes Muster umschreiben als auch melden, was sie geändert hat, sodass sich Erkennung und Migration ein einziges Prädikat teilen und nicht auseinanderdriften können. Führen Sie die Erkennung aus, indem Sie dieselben Funktionen auf eine Wegwerfkopie anwenden, sodass eine Warnung genau dann ausgelöst wird, wenn ein tatsächlich schreibendes Update die Datei ändern würde; fixieren Sie diese Äquivalenz mit einem Test.
### Den Zeitpunkt jeder Regel klassifizieren
Legen Sie pro Regel fest, wann ein Umschreiben greift: bei jedem Laden, nur bei einem expliziten Update, still ohne Warnung, oder als vorgemerkte Vorgabe, die ein späteres Release übernimmt. Lassen Sie einen Wert, der sich nicht sicher umschreiben lässt (fehlerhaft, oder ein Zielschlüssel bereits belegt), unangetastet, und überlassen Sie es dem eigenen Typfehler des Parsers, die Meldung zu tragen.
## Accessoren so benennen, dass das Präfix die Kosten zeigt, und den Ort des Caches festlegen
Kodieren Sie die Absicht in den Namen von Accessoren, damit ein Aufrufer allein aus dem Präfix Kosten und Fehlerverhalten vorhersagen kann.
### Das Präfix den Vertrag tragen lassen
Übernehmen Sie ein einheitliches Schema: ein bloßes Substantiv liefert einen Wert ohne Seiteneffekte und darf cachen; ein mit „set“ beginnender Name schreibt Zustand und kann fehlschlagen; ein mit „require“ beginnender Name schlägt fehl, wenn der Wert fehlt; ein mit „fetch“ beginnender Name führt Netzwerk-I/O aus; ein mit „load“ beginnender Name führt Datei-I/O aus. Vermeiden Sie ein überflüssiges „get“-Präfix, wo ein bloßes Substantiv sich besser liest.
### Festlegen, wo zwischengespeicherter Zustand lebt
Teilen Sie schreibgeschützte, zwischengespeicherte Werte über einen referenzgezählten Cache, den ein Klonen gemeinsam nutzt. Wählen Sie die Stufe nach Kosten und Nutzungshäufigkeit: billige, häufig genutzte Werte bekommen ein In-Memory-„get-or-create“; teure Werte werden auf die Festplatte persistiert; Werte, auf die beides zutrifft, behalten eine In-Memory-Front über einem persistenten Hintergrund. Dokumentieren Sie, was gecacht wird und was nicht, sowie die Invariante für die Aktualisierung nach einer Mutation.
