Weniger Code, mehr Ursache: Mein Prompt gegen Codex-Overengineering in PHP und Symfony
Wer heute mit Coding Agents arbeitet, kennt das Muster. Man meldet einen überschaubaren Fehler, etwa einen falsch gesetzten Regex, einen ungünstigen Vergleich oder einen fehlerhaften Sonderfall im Request-Handling, und bekommt als Antwort eine kleine Architektur-Offensive. Neue Services, neue Helper, neue Abstraktionen, zusätzliche Fallbacks, defensive Defaults, stilles „Reparieren“ ungültiger Zustände. Der eigentliche Fehler ist danach manchmal weg. Das eigentliche Problem fast nie.
Gerade bei PHP- und Symfony-Projekten ist das unerquicklich. Nicht, weil Struktur oder saubere Architektur unwichtig wären, sondern weil ein Agent sehr leicht dazu neigt, Aktivität mit Qualität zu verwechseln. Viel Code sieht entschlossen aus. Wenig Code, der exakt an der Ursache ansetzt, sieht dagegen unspektakulär aus. In produktiven Systemen ist fast immer die zweite Variante die bessere.
Ich habe mir deshalb einen festen Steuerungs-Prompt gebaut, den ich einem Codex-Agenten voranstelle. Sein Ziel ist nicht, besonders elegant zu klingen. Er soll den Agenten disziplinieren. Er soll ihn zwingen, klein zu denken, direkt an die Ursache zu gehen und vor allem eines zu unterlassen: stillschweigende Reparaturversuche.
Das eigentliche Problem mit Agenten-Code
Das Kernproblem ist nicht, dass Agenten Fehler machen. Das tun Menschen auch. Das Problem ist die typische Richtung des Fehlers.
Ein Agent fasst einen Defekt oft nicht als lokale Ursache auf, sondern als Anlass, das System großflächig „robuster“ zu machen. Aus einem simplen Bugfix wird dann ein Refactoring mit Beifang. Hier ein zusätzlicher Normalizer, dort eine Abstraktionsschicht, dazu noch ein Fallback für den Fall, dass künftig irgendetwas fehlen könnte. Das Ergebnis ist Code, der auf den ersten Blick umsichtig wirkt, tatsächlich aber die Komplexität erhöht, Verantwortlichkeiten verwischt und den wahren Fehler verdeckt.
Dazu kommt ein zweites Muster: Es wird zu spät gefixt. Statt die Zeile zu korrigieren, an der der falsche Wert entsteht, wird am Ende der Pipeline geprüft, ob etwas „komisch aussieht“. Das ist bequem, aber technisch schwach. Denn der kaputte Zustand lebt dann bereits durch mehrere Verarbeitungsschritte hindurch, verursacht Seiteneffekte und macht das Debugging schwerer.
Die dritte Unsitte ist die Fallback-Kultur. Fehlende Werte werden ersetzt. Ungültige Formate werden „best effort“ interpretiert. Null wird geduldet, obwohl Null fachlich gar nicht zulässig ist. Nichts kracht. Alles läuft irgendwie weiter. Genau das ist in vielen Business-Anwendungen gefährlich. Ein still reparierter Fehler ist kein gelöster Fehler, sondern ein verschobener.
Warum „fail fast“ in Business-Code meist die bessere Entscheidung ist
In vielen Teams hat sich über Jahre eine Kultur des Schonens eingeschlichen. Systeme sollen tolerant sein, Eingaben möglichst abfangen, Prozesse möglichst nicht abbrechen. Das klingt vernünftig, ist aber nur dann sinnvoll, wenn diese Toleranz fachlich gewollt und sauber modelliert ist.
In internen Produktionssystemen, Importstrecken, Publisher-Pipelines oder Symfony-Backends mit klarer Verantwortung ist das Gegenteil oft richtiger. Wenn ein Pflichtwert fehlt, ist das kein Anlass für einen Ersatzwert. Es ist ein Fehler. Wenn ein Zustand logisch nicht vorkommen darf, dann sollte er nicht weich behandelt werden. Er sollte laut scheitern.
Fail-fast hat einen großen Vorteil: Fehler bleiben lokal. Sie entstehen an einer Stelle und werden dort sichtbar. Das spart Zeit, reduziert Streuung und verhindert, dass kaputte Zustände an anderer Stelle scheinbar plausibel weiterverarbeitet werden.
Ein Agent muss dazu explizit gezwungen werden. Sonst optimiert er auf „läuft irgendwie“ statt auf „ist fachlich sauber“.
Mein Prompt: kein Architekturtheater, sondern Disziplin
Der Prompt, den ich verwende, formuliert deshalb keine stilistischen Wünsche, sondern harte Arbeitsregeln. Er positioniert den Agenten als pragmatischen Senior-PHP/Symfony-Engineer mit Minimal-Change-Fokus. Das klingt banal, ist aber entscheidend. Ein Agent braucht eine klare Rolle. Sonst liefert er gerne generische Lehrbuchlösungen.
Im Zentrum stehen einige einfache, aber wirksame Prinzipien:
Erstens: Root Cause zuerst. Nicht dort reparieren, wo das Symptom sichtbar wird, sondern dort, wo der falsche Zustand entsteht.
Zweitens: minimaler Diff. So wenige Zeilen wie möglich, keine unnötigen Umbauten, keine prophylaktische Modernisierung.
Drittens: keine neuen Dateien und keine neuen Klassen, solange der Bug ohne sie behoben werden kann. Das bremst den Drang zur künstlichen Verallgemeinerung.
Viertens: klare Kontrollstrukturen statt „ternärer Hölle“. Das ist nicht nur Geschmacksfrage. Kompakter Code wirkt auf Agenten oft elegant, wird aber bei realen Fehlerbildern schnell undurchsichtig.
Fünftens: kein Nebenbei-Refactoring. Ein Bugfix ist kein Vorwand, um halb daneben die Architektur umzubauen.
Sechstens: keine Defaults, keine Fallbacks, keine stillen Workarounds. Wenn etwas fehlt, ist das kein Anlass für Kulanz, sondern für einen expliziten Fehler.
Gerade dieser letzte Punkt verändert die Qualität der Antworten spürbar. Der Agent hört auf, den kaputten Zustand zu kaschieren, und beginnt, ihn ernst zu nehmen.
Warum dieser Ansatz gerade zu Symfony passt
Symfony ist stark genug, um fast jede Art von Abstraktion zu ermöglichen. Genau deshalb ist Disziplin wichtig. Wer in Symfony arbeitet, kann für ein kleines Problem sehr schnell einen großen Rahmen bauen: eigener Validator, zusätzlicher Service, neue DTO-Schicht, Listener, Normalizer, Exception Mapper. Alles davon kann in bestimmten Fällen richtig sein. Vieles davon ist bei kleinen Defekten unnötig.
Ein guter Prompt muss deshalb nicht anti-Framework sein. Er muss den Agenten nur daran erinnern, dass bestehende Projektmuster zu respektieren sind. Wenn es bereits Validatoren, Services oder Fehlermechanismen gibt, sollen diese genutzt werden. Wenn nicht, soll nicht reflexhaft neue Infrastruktur entstehen.
Das ist keine Absage an Struktur. Es ist eine Absage an unnötige Struktur.
Der Output ist Teil der Qualität
Ein oft unterschätzter Punkt ist das Ausgabeformat. Viele Agenten-Antworten scheitern nicht nur am Code, sondern schon an der Art, wie sie denken. Sie springen zu schnell in die Lösung, ohne die Ursache sauber zu benennen. Oder sie produzieren seitenlange Begründungen, aus denen am Ende kaum hervorgeht, was genau geändert werden soll.
Deshalb erzwingt mein Prompt auch ein festes Format: erst die Diagnose, dann die Fix-Strategie, dann der Patch, dann die Risiken, dann eine kurze Checkliste. Das zwingt den Agenten, seine eigene Lösung gedanklich zu ordnen. Vor allem aber verhindert es, dass er unter einem Berg wohlklingender Erklärungen einen unnötig großen Umbau versteckt.
Ein knapper, präziser Diagnoseblock ist oft der beste Indikator dafür, ob der Agent das Problem wirklich verstanden hat. Wenn die Ursache dort nicht konkret benannt wird, ist der Rest meist ebenfalls unscharf.
Was sich in der Praxis verbessert
Seit ich so arbeite, fällt ein Teil der typischen Agenten-Auswüchse deutlich seltener auf. Die Lösungen werden kleiner. Fehler werden öfter dort behoben, wo sie entstehen. Es werden weniger neue Klassen erfunden. Es gibt weniger „defensive“ Codepfade, die später niemand mehr versteht. Und vor allem: Der erzeugte Code bleibt näher an der bestehenden Anwendung.
Das ist in gewachsenen PHP- und Symfony-Systemen zentral. Nicht jeder Bugfix muss ein Statement über Softwarearchitektur sein. Meistens soll er einfach nur den Fehler korrekt beseitigen, ohne das Umfeld aufzublasen.
Natürlich verhindert auch ein guter Prompt keine schlechten Antworten. Aber er verschiebt den Grundimpuls des Agenten. Weg vom Vorzeigen, hin zur Präzision.
Der eigentliche Gewinn: weniger Eitelkeit im Code
Ich halte „Angebercode“ inzwischen für einen recht treffenden Begriff. Gemeint ist nicht guter, ambitionierter Code, sondern Code, der mehr zeigen will, als die Aufgabe verlangt. Solcher Code ist für Agenten verführerisch, weil er nach Kompetenz aussieht. Tatsächlich ist er oft nur laut.
In produktionsnahen Systemen ist Lautstärke kein Qualitätsmerkmal. Gute Änderungen sind häufig still. Eine Zeile im Regex. Ein früher Guard. Ein harter Fehler an der richtigen Stelle. Ein entfernter Fallback, der bisher ein Datenproblem verdeckt hat. Das alles sieht in einem Screenshot wenig spektakulär aus. Für Wartbarkeit, Debugging und Betrieb ist es oft genau richtig.
Wer mit Coding Agents arbeitet, sollte sie deshalb nicht nur auffordern, ein Problem zu lösen. Er sollte sie auf eine Arbeitsmoral festnageln. Minimal invasive Änderungen. Root Cause statt Symptompflaster. Keine unnötigen Klassen. Keine ternären Gymnastikübungen. Keine stillen Defaults. Kein freundliches Weiterlaufen in kaputten Zuständen.
Ein Agent wird dadurch nicht kreativer. Aber nützlicher.
Der Prompt
Den Prompt selbst verwende ich in dieser Form:
Act like a pragmatischer Senior-PHP/Symfony-Engineer und “Minimal-Change”-Reviewer für Codex.
Ziel
Du sollst mir bei PHP- und Symfony-Änderungen helfen, ohne Scope-Drift, ohne Über-Engineering, ohne “Angebercode”. Priorität: kleinste korrekte Änderung, die die Root Cause behebt — und bei fehlenden Inputs/Fehlern IMMER hart fehlschlägt (fail-fast), niemals stillschweigend “repariert”.
Aufgabe
Wenn ich dir ein Problem, Codeausschnitte oder Fehlerlogs gebe, liefere eine minimal-invasive Lösung, die direkt die Ursache behebt (nicht nur Symptome downstream). Keine Defaults/Fallbacks, keine stillen Workarounds: Wenn etwas fehlt oder fehlschlägt, muss ein expliziter Fehler entstehen.
Arbeitsregeln (nicht verhandelbar)
1) Root Cause zuerst: Finde die Stelle, an der der Fehler entsteht. Fixe dort. Keine “Pipeline-Fixes”, wenn ein einzelner Regex/Condition/Edge-Case die Ursache ist.
2) Minimaler Diff: Ändere so wenige Zeilen wie möglich. Keine Umstrukturierung “zur Sicherheit”.
3) Keine neuen Dateien/Klassen/Abstraktionen, außer zwingend nötig. Standard: keine neue Klasse.
4) Klarer Code statt ternäre Hölle: Early Returns, klare if-Blöcke, sprechende Variablennamen. Vermeide Verschachtelung; keine cleveren Einzeiler.
5) Kein Nebenbei-Refactoring: Refactoring nur, wenn es den Bug-Fix direkt ermöglicht oder die Änderung sonst unverständlich wäre.
6) Fail-fast statt Fallbacks:
- Keine Default-Werte, keine stillen Null-Coalescings als “Reparatur”, keine “best effort”-Konvertierungen.
- Wenn Input/State ungültig ist: Exception werfen (oder vorhandenen Fehlermechanismus nutzen), mit klarer Message.
- Keine swallow/catch-and-continue. Catch nur, wenn du danach wieder wirfst oder in einen härteren, domänenspezifischen Fehler übersetzt.
- Validierung früh (Guard Clauses). Bei Symfony: nutze vorhandene Validatoren/Constraints oder wirf direkt, aber ohne neue Framework-Schichten zu bauen.
7) Symfony-Pragmatismus: Nutze bestehende Patterns des Projekts. Respektiere vorhandene Services/Validatoren/Normalizers statt neue Architekturen zu erfinden.
8) Tests: Nur die kleinste sinnvolle Absicherung. Wenn Test existiert: erweitern. Wenn nicht: einen kleinen Test hinzufügen ODER kurz begründen, warum keiner sinnvoll ist. Tests sollen ebenfalls fail-fast sein (keine “graceful degradation”).
Output-Format (immer)
A) Diagnose in 2–4 Sätzen: Ursache + exakte Stelle/Mechanismus.
B) Fix-Strategie in 1–3 Bullets: warum minimal, korrekt, fail-fast.
C) Patch: konkreter Code (Diff-ähnlich oder klar markierte Snippets).
D) Risiken/Edge-Cases: max. 3 Bullets (inkl. “welche Inputs jetzt hart fehlschlagen”).
E) Mini-Checkliste: “kompiliert?”, “läuft?”, “Test/Scenario”, “Fehlerpfad geprüft?”.
Wenn Informationen fehlen, mache keine stillen Annahmen im Code. Stattdessen:
- Markiere max. 2 Annahmen im Text,
- und implementiere den Code so, dass er bei falschen Annahmen hart fehlschlägt.
Take a deep breath and work on this problem step-by-step.
Für meinen Alltag ist das kein Schönwetter-Prompt, sondern ein Schutzgeländer. Nicht gegen Fehler. Gegen unnötigen Code.
0 Kommentare