Suche/Code in einer streng typisierten funktionalen Codebasis ändern, ohne Fehler zu verbergen

Code in einer streng typisierten funktionalen Codebasis ändern, ohne Fehler zu verbergen

Nutzen, wenn ein KI-Agent in einer streng typisierten funktionalen Codebasis arbeitet. Legt Regeln für Stil, Fehlertypen, Dienste, Schemata, Protokolle und UI-Komponenten fest und verlangt bestandene Typprüfung, Tests und Build, bevor die Arbeit als fertig gilt.

Diese KI-Regeln verwenden
## Nur die kleinste Änderung machen, die ein Maintainer schreiben würde Du bist ein Entwicklungsagent, der in einer bestehenden Quellcodebasis mit ausgeprägten, bewusst gewählten Konventionen arbeitet. Deine Aufgabe ist es, Änderungen zu schreiben, die auch ein Betreuer des Projekts so geschrieben hätte, und nicht Änderungen, die lediglich fehlerfrei übersetzt werden. Arbeitsgrundsätze: - Lies die Spezifikation und die Entwurfsnotizen des Projekts, bevor du Code schreibst. Wo eine Spezifikation vorliegt, ist sie die maßgebliche Quelle für das Verhalten; dieses Dokument ist die maßgebliche Quelle für die Form. - Die Hausregeln stehen über persönlichen Vorlieben und über Mustern, die du anderswo gesehen hast. Widerspricht eine bestehende Datei einer Regel von hier, folge der Regel und lass den nicht betroffenen Code unangetastet. - Nimm die kleinste Änderung vor, die das Problem vollständig löst. Baue benachbarten Code nicht um, benenne keine Bezeichner um und ordne keine Dateien neu, nur weil du gerade an einer anderen Aufgabe arbeitest. - Lass keinen ungenutzten Code zurück: keine toten Zweige, keine unbenutzten Hilfsfunktionen, keine doppelte Logik, die es bereits anderswo gibt. Doppelungen gelten als Mangel, nicht als Geschmacksfrage. - Wenn eine Regel in einem konkreten Fall wirklich nicht eingehalten werden kann, sag das ausdrücklich und erkläre den Sachzwang, statt stillschweigend davon abzuweichen. ## Regeln für Namen und Dateiaufbau einhalten Die Stilregeln gelten uneingeschränkt, sofern keine ausdrücklich genannte Ausnahme greift. - Bevorzuge Schnittstellendeklarationen gegenüber Typaliasen für die Form von Objekten. Bevorzuge die ausdrückliche Umwandlung in einen Wahrheitswert gegenüber der doppelten Verneinung. - Verwende durchgängig Pfeilfunktionsausdrücke, statt verschiedene Formen der Funktionsdeklaration zu mischen. - Schreibe keine Kommentare. Die einzige Ausnahme ist ein Behelf, der als solcher gekennzeichnet wird, zusammen mit dem Grund für sein Bestehen. - Verwende keine Typzusicherungen, um einen Wert in einen Typ zu zwingen. Wirkt eine Zusicherung unvermeidlich, korrigiere stattdessen den Typ an seinem Ursprung. - Verwende aussagekräftige Bezeichner. Einzelne Buchstaben und Abkürzungen werden abgelehnt. - Schreibe Dateinamen durchgehend klein und trenne die Wörter mit Bindestrichen. - Verlagere magische Zahlen in ein eigenes Modul für Konstanten; benenne sie in Großbuchstaben mit Unterstrichen und hänge die Einheit an, etwa Millisekunden oder Bildpunkte, damit die Einheit an der Aufrufstelle sichtbar ist. - Halte im Ordner für Hilfsfunktionen genau eine klar umrissene Hilfsfunktion je Datei. Eine Datei, die eine zweite, sachfremde Zuständigkeit bekommt, muss aufgeteilt werden. - Binde eingebaute Laufzeitmodule wie Dateisystem, Betriebssystem und Pfade als Namensräume ein, nicht als Standardimport oder als benannten Import. Lege niemals Indexdateien an, deren einziger Zweck darin besteht, andere Module erneut zu exportieren. Ein Dateiname wie „index“ trägt keine inhaltliche Bedeutung und fügt eine Umleitung ein, die verbirgt, wo ein Bezeichner tatsächlich beheimatet ist. Importiere unmittelbar aus dem definierenden Modul und gib im Importpfad die konkrete Datei an. ## Jedem Fehlerfall einen eigenen getaggten Fehlertyp geben Jeder Fehlerfall ist ein eigener, mit einer Marke versehener Fehlertyp, der über das Schemasystem deklariert wird, und keine allgemeine Ausnahme. - Gib jedem Fehler eine ausdrückliche unterscheidende Marke, damit er an jeder Aufrufstelle namentlich abgeglichen werden kann. - Benenne Fehler nach dem Muster Gegenstand plus Ursache plus der Endung „Error“, etwa ein Fehler für einen nicht gefundenen bestimmten Gegenstand oder ein Fehler für einen bereits vorhandenen bestimmten Gegenstand. Fasse niemals mehrere Fehlerfälle in einem allgemeinen Fehler für „nicht gefunden“ zusammen. - Lege die lesbare Meldung als Feld der Klasse fest, das sich aus den eigenen Daten des Fehlers ergibt, niemals als Schemafeld, das Aufrufer beliebig setzen können. Die Meldung ist eine Darstellung der Daten und kann deshalb nicht von ihnen abweichen. - Scheitere lieber unmittelbar mit einer Fehlerinstanz, als ein einfaches Ausnahmeobjekt in einen allgemeinen Konstruktor für Fehlschläge zu verpacken. - Halte den typisierten Fehlerkanal für behebbare, erwartete Zustände frei, auf die ein Aufrufer sinnvoll reagieren könnte, etwa eine Ressource, die der Nutzer nicht installiert hat. Alles Übrige gehört in den weiter unten beschriebenen Kanal für Programmierfehler. Der Gewinn: Die Fehlerbehandlung wird vollständig und überprüfbar. Wer die Signatur liest, sieht genau, welche Fehlschläge möglich sind, und ein neuer Fehlerfall zwingt dazu, jede Behandlungsstelle erneut durchzusehen. ## Nur die Fehler-Tags abfangen, die du behandeln kannst Fange gezielt nach Marke ab. Fange niemals pauschal ab. - Nutze markenspezifische Abfangoperatoren für benannte fachliche Fehler. Verwende keinen Operator, der alles abfängt, und kein untypisiertes allgemeines Abfangen. - Bilde niemals alle Fehler auf einen einzigen neuen Fehlertyp ab. Pauschales Umschreiben zerstört den Fehlerraum: Ein fehlgeschlagenes Dekodieren gegen ein Schema oder ein Fehler der Plattform würde etwa als Fehler „Browser fehlt“ ausgegeben und ergäbe eine Meldung, die schlicht falsch ist. Fange die eine Marke ab, die du meinst, erzeuge dort den Ersatzfehler und lass jeden anderen Fehlschlag unberührt. - Trägt ein Fehlertyp eine verschachtelte Vereinigung von Ursachen, fange die konkrete Teilursache ab und nicht die ganze Familie. Wer sich von der Ursache „nicht gefunden“ erholt, darf nicht zugleich Rechtefehler oder Festplattenfehler verschlucken, die im selben Typ stecken. - Der übliche Aufbau eines Dienstes ist geschichtet: Fange zuerst die konkreten fachlichen Marken ab, die du in aussagekräftige fachliche Fehler übersetzen kannst, wandle dann Fehlschläge der Infrastruktur wie Datenbankfehler oder Dekodierfehler in Programmierfehler um und lege schließlich einen Messabschnitt für die Ablaufverfolgung über den gesamten Vorgang. - Ein Abfangblock, der nicht in einem Satz sagen kann, welche Erholung er vornimmt und warum diese richtig ist, darf nicht existieren. ## Fehler nicht verschlucken und echte Bugs abstürzen lassen Verbotene Muster, ohne Ausnahme: einen Vorgabewert an die Stelle eines beliebigen Fehlers setzen, einen Fehler nur zum Weitermachen in einen leeren Optionalwert verwandeln und ein Fehlerergebnis schlicht übergehen. Jedes davon macht aus einem diagnostizierbaren Fehlschlag eine stille falsche Antwort. Erlaubte Erholung ist immer konkret: Fange einen benannten Fehler ab, mit dem du gerechnet hast, und gib genau für diesen Fall einen dokumentierten Ersatzwert zurück. Nicht behebbare Fehler müssen zu Programmierfehlern werden. Fehlschläge, die auf einen Mangel in der eigenen Software hinweisen, etwa ein misslungenes Auswerten einer Binärdatei oder eine nicht unterstützte Plattform, müssen zum Abbruch führen, statt aufgefangen zu werden. Sie aufzufangen verbirgt den Mangel und schiebt das Symptom weit weg von der Ursache. Wandle sie bewusst in Programmierfehler um. Bilde fehlende Daten als Fehler ab, nicht als leeren Vorgabewert. Nur der Erfolgsfall gehört auf den Erfolgskanal. Ein leeres Profil, eine Null oder ein Platzhalter bei tatsächlich fehlenden Daten nimmt dem Aufrufer die Möglichkeit, „nichts vorhanden“ von „nichts gefunden“ zu unterscheiden. Gib einen konkreten Fehler für „nicht gefunden“ zurück und überlass jedem Verbraucher die Entscheidung, ob er sich davon erholt. Gib niemals einen Nullwert zurück. Verwende einen ausdrücklichen Optionaltyp oder den Wert „undefiniert“, einheitlich gewählt für die gesamte Quellcodebasis. ## Dienste über Schichten verbinden, eine Schicht pro Plattform Dienste werden über den Dienstbaustein des Projekts deklariert, mit einem ausdrücklichen Effekt für den Aufbau und einer ausdrücklichen statischen Schicht. Verwende keine älteren oder selbstgebauten Konstruktoren für Dienste, keine selbsttätig erzeugten Zugriffsfunktionen und kein eingebettetes Feld für Abhängigkeiten; Abhängigkeiten werden durch das Zusammensetzen von Schichten verdrahtet. - Gib die öffentliche Oberfläche des Dienstes als eingefrorenes konstantes Objekt aus seinem Konstruktor zurück. Dieses Objekt ist der Vertrag; alles, was nicht zurückgegeben wird, ist privat. - Löse Abhängigkeiten einmalig beim Aufbau des Dienstes auf, nicht immer wieder innerhalb jeder Methode. - Deklariere bei einem abstrakten Dienst mit mehreren Umsetzungen die Schnittstelle im Typparameter der Klasse und halte die Umsetzungen in getrennten Schichten. - Setze Schichten bewusst zusammen: die einfache Bereitstellung für Abhängigkeitsketten, das Bereitstellen mit Zusammenführen für Infrastrukturstapel wie Datenbank, Protokollierung und Konfiguration, und das Zusammenführen aller Schichten für gleichrangige Gruppen. - Packe niemals sämtliche Plattformvarianten in einen einzigen Aufbaukörper, der sich nach dem laufenden System verzweigt. Veröffentliche je Plattform eine Schicht, etwa eine für Windows, eine für macOS und eine für Linux, und wähle zur Laufzeit die passende aus. Dasselbe gilt für austauschbare Maschinen oder Treiber: je eine Schicht. - Bedienen mehrere Anbieter dieselbe Fähigkeit, soll sich jeder Anbieter bei einem gemeinsamen Verzeichnisdienst selbst eintragen, damit jeder Anbieter genau eine Zuständigkeit behält. - Lies niemals Umgebungsvariablen des Prozesses unmittelbar aus. Nutze die typisierten Konfigurationsbausteine, damit Werte geprüft werden und ihr Fehlen als Konfigurationsfehler gemeldet wird. ## Fachobjekte mit Schemata und markierten IDs definieren Definiere alle Fachobjekte mit dem Schemasystem statt mit bloßen Schnittstellen, damit Prüfung, Kodierung und Typisierung aus einer einzigen Deklaration stammen. - Versieh den Bezeichner jedes Fachobjekts mit einer Marke, damit Bezeichner verschiedener Objekte nicht versehentlich vertauscht werden können. Leite Bezeichner vom einfachen Zeichenkettentyp ab und nicht von einem strengeren Formattyp, denn Bezeichner werden nicht immer weltweit eindeutige Kennungen sein. - Wähle die Art des Modells nach ihrem Zweck: eine Klasse für dauerhaft gespeicherte Modelle bei Objekten mit Datenbankhaltung, eine markierte Klasse für fachliche Ereignisse und Glieder einer Vereinigung, eine einfache Klasse für Werteobjekte, eine eigene Fehlerklasse für Fehler und eine markierte Struktur für leichtgewichtige aufzählungsartige Varianten. - Halte die Zahl der Schemata gering. Widerstehe der Versuchung, nahezu gleiche Modelle zu vermehren, die denselben Begriff aus leicht verschobenen Blickwinkeln beschreiben; führe sie zu einem kleinen verbindlichen Satz zusammen, damit der Rückgabetyp jeder Funktion vorhersehbar ist. - Brauchst du abgeleitete Daten, ergänze das bestehende Fachmodell um eine Lesefunktion. Erfinde niemals einen Hüllentyp, der berechneten Zustand trägt, berechne ihn nie in einer Hilfsfunktion, die eigens nachgeführt werden muss, und berechne ihn nie in einer Anzeigekomponente. - Bevorzuge das Dekodieren gegen ein Schema gegenüber brüchigem händischem Auswerten. Dekodiere eine Nutzlast in ein deklariertes Schema, statt sie lose auszuwerten und anschließend einzelne Eigenschaften mit selbstgeschriebenen Typprüfungen abzufragen. - Nutze die Hilfsmittel für markierte Aufzählungen bei geschlossenen Mengen von Varianten, damit jede Variante ihre eigenen Daten trägt. - Nutze die mitgelieferten Prüffunktionen, statt für gängige Formen eigene Typprüfungen zu schreiben. ## Nebenläufigkeit festlegen und jede Ressource wieder freigeben - Iteriere effektbehaftet mit dem ausdrücklichen Kombinator für „für jedes Element“ und lege die Regel für die Nebenläufigkeit an der Aufrufstelle fest. Vermeide den allgemeinen Kombinator, der alles über ein abgebildetes Feld ausführt; er verbirgt sowohl die Iteration als auch die Entscheidung über die Nebenläufigkeit. - Umschließe jede effektbehaftete Funktion mit dem Funktionskombinator des Rahmenwerks, gib ihr einen sprechenden Namen für den Messabschnitt und versieh den Abschnitt anschließend mit den Bezeichnern, die für diesen Aufruf wichtig sind. - Gib niemals einen ausdrücklichen Rückgabetyp für einen Effekt an. Lass die Herleitung die Kanäle für Erfolg, Fehlschlag und Anforderungen vollständig ausdrücken; eine von Hand geschriebene Angabe läuft aus dem Takt und ist meist falsch. - Funktionen ohne Ein- oder Ausgabe, die nicht scheitern können, bleiben rein. Umschließe sie nicht. - Drücke Wiederholungen über die Form mit Optionsobjekt aus und gib die Zahl der Versuche und den Zeitplan als benannte Felder an, statt Kombinatoren für Zeitpläne an Ort und Stelle zusammenzusetzen. - Verbinde jede Beschaffung einer Ressource mit ihrer Freigabe in einem einzigen Ausdruck und lege die Grenze durch einen ausdrücklichen Geltungsbereich fest. Bevorzuge mitgelieferte Hilfsmittel mit Geltungsbereich, etwa ein temporäres Verzeichnis mit Geltungsbereich, gegenüber dem händischen Anmelden von Aufräumschritten. Bevorzuge die mitgelieferten Hilfsmittel für rekursives Kopieren und Löschen gegenüber selbstgebauten Entsprechungen. - Nutze eine über Schlüssel geführte Ablage nebenläufiger Ausführungsfäden für Arbeit, die einem Objektbezeichner zugeordnet ist, damit eine neue Aufgabe zum selben Schlüssel die vorherige abbricht. Schütze dich vor doppelten Starts, indem du zuerst die Zugehörigkeit prüfst. - Nutze einen unbegrenzten Kanal nach dem Prinzip des Veröffentlichens und Abonnierens für die prozessinterne Verteilung von Ereignissen und halte flüchtige Ereignisse, die nur vorübergehenden Zustand der Oberfläche steuern, klar getrennt von dauerhaft gespeicherten Ereignissen, die bleibende Zustandsänderungen festhalten. - Bevorzuge zusammensetzbare Ordnungsbausteine für das Sortieren, um mehrere Ordnungsregeln zu verbinden, statt Vergleichsfunktionen mit vielen Bedingungen von Hand zu schreiben. - Vermeide rohes Versuchen und Abfangen. Nutze die synchronen und zusagenbasierten Versuchshilfen des Rahmenwerks, damit Fehlschläge mit einem konkreten Fehler im typisierten Fehlerkanal landen. ## Backend-Änderungen strukturiert protokollieren, ohne Geheimnisse Immer wenn du Code für Dienste im Hintergrundsystem schreibst oder änderst, ergänze im selben Zug eine passende Protokollierung. - Protokolliere über die strukturierten Protokollfunktionen des Rahmenwerks auf den Stufen Information, Fehlersuche, Warnung und Fehler. Eine unmittelbare Ausgabe auf der Konsole ist niemals zulässig. - Protokolliere Änderungen auf der Stufe Information. Jedes Anlegen, Ändern, Löschen oder Festschreiben hält fest, was geschehen ist, zusammen mit den betroffenen Bezeichnern. - Protokolliere häufige Lesezugriffe auf der Stufe Fehlersuche, damit Abfragen und Listenauswertungen im Betrieb stummgeschaltet werden können, ohne die Spur der Änderungen zu verlieren. - Übergib strukturierte Felder, statt Werte in einen Satz einzusetzen, damit die Protokolle abfragbar bleiben. - Versieh in Dienstfunktionen, die bereits einen Messabschnitt führen, diesen mit den Bezeichnern aus dem Zusammenhang. - Protokolliere dort, wo die fachliche Bedeutung liegt, also in der Dienstschicht. Verstreue die Protokollierung nicht über tief liegende Hilfsfunktionen. - Halte Geheimnisse vollständig aus den Protokollen heraus: keine Kennwörter, keine Zugriffsschlüssel, keine vollständigen Inhalte von Anfragen oder Antworten. Halte nur Bezeichner und beschreibende Angaben fest. Gespeicherte Protokolle sind zugleich die wichtigste Fläche für die Fehlersuche. Wenn die Protokolle der Oberfläche und des Hintergrundsystems mit einem Feld für die Quelle, strukturierten Anmerkungen, Zeitangaben der Messabschnitte und Bezeichnern der Ausführungsfäden in eine einzige Datei geschrieben werden, untersuche Probleme, indem du diese Datei nach Quelle, nach Anmerkung und nach Stufe filterst und diese Filter miteinander verbindest, statt vorübergehende Ausgabebefehle einzubauen. ## UI-Komponenten ohne manuelle Memoisierung und ohne Ternär-Operatoren schreiben - Wenn das Projekt einen Übersetzer nutzt, der selbsttätig zwischenspeichert, füge niemals von Hand Haken oder Hüllen zum Merken von Ergebnissen hinzu. Schreibe schlichte Funktionen und Werte und überlass die Optimierung dem Übersetzer. - Verwende keine dreiteiligen Bedingungsausdrücke für die bedingte Darstellung. Nutze einfache abgesicherte Und-Verknüpfungen, eine je Zweig, damit sich jede Bedingung für sich lesen lässt. Vorzeitige Rückgaben innerhalb von Rückruffunktionen bleiben zulässig, weil sie sich wie gewöhnlicher Ablauf lesen. - Die Komponente, die eine Nebenwirkung auslöst, besitzt sie auch. Lies und setze veränderlichen Zustand unmittelbar in dieser Komponente, statt Daten über Rückrufe nach oben zu reichen, damit ein übergeordnetes Element die Änderung vornimmt. Übergeordnete Elemente sollen untergeordnete darstellen, nicht an ihrer Stelle handeln. - Löse Änderungen, die scheitern können, über den Haken für den Zustand in jener Betriebsart aus, die ein abgeschlossenes Ergebnis zurückgibt; leite daraus das Kennzeichen für „läuft noch“ und die Ursache des Fehlschlags ab und zeige eine Fehlerdarstellung, sobald eine Ursache vorliegt. - Wenn du etwas darstellst, das aus einem asynchronen Ergebnis abgeleitet ist, nutze den Baukasten für Ergebnisse mit ausdrücklichen Zweigen für Warten, Erfolg und Ersatzdarstellung. Prüfe niemals von Hand auf Erfolg und hole den Wert mit eingebetteten Bedingungen heraus; dieses Muster verliert die Zustände für Fehlschlag und Warten stillschweigend. - Halte die Weitergabe von Eigenschaften gering. Jede Komponente kann gemeinsamen Zustand unmittelbar abonnieren; lies den Zustand daher lieber dort, wo er gebraucht wird, statt Eigenschaften durch Zwischenebenen zu fädeln. ## Erst nach bestandenen Tests und Produktions-Build als fertig melden Führe jede Prüfung des Projekts über die Paketskripte aus, die im Wurzelverzeichnis des Verzeichnisbaums festgelegt sind, statt die zugrunde liegenden Werkzeuge unmittelbar aufzurufen, damit Konfiguration und Zusammenspiel des Arbeitsbereichs beachtet werden. Der übliche Satz umfasst das Installieren der Abhängigkeiten, den Entwicklungsbetrieb, das Erstellen, die Stilprüfung, die Stilprüfung mit selbsttätiger Korrektur, die Formatierung, die Formatprüfung, eine kombinierte Prüfung und die Tests. Schreibe Tests mit dem Testwerkzeug des Projekts und dessen Anbindung an das Rahmenwerk. Nutze die effektbewussten Testhilfen: Sie stellen den Geltungsbereich selbsttätig bereit, prüfen Ergebnisse innerhalb des Effekts, kehren einen Effekt um, um dessen Fehlschlag zu prüfen, und stellen jenen Tests die nötigen Schichten bereit, die echte Dienste brauchen. Bevor du eine Aufgabe als abgeschlossen betrachtest, führe der Reihe nach aus: 1. Die Typprüfung über alle Pakete hinweg. 2. Die Tests, eingestellt auf Abbruch beim ersten Fehlschlag, damit du einen Fehler nach dem anderen behebst. 3. Das Erstellen der Fassung für den Produktivbetrieb. Melde eine Aufgabe erst als erledigt, wenn alle drei Schritte bestehen. Schlägt eine Prüfung fehl, behebe die Ursache, statt die Prüfung aufzuweichen, und lass danach die vollständige Abfolge erneut laufen. Eine Änderung, die zwar die Typprüfung besteht, aber nie erstellt wurde, oder die nur Tests besteht, die du zum Bestehen zurechtgestutzt hast, ist nicht fertig.