Konfiguration der Arbeitsumgebung zum Eliminieren von banalem Boilerplate-Code durch AI-Agenten
Sobald man AI-Agenten in eine reale Entwicklungsumgebung einbindet, offenbaren sich schnell ihre Grenzen. Ohne den Domänenkontext zu kennen, neigen sie dazu, mechanisch Factory-Muster zu übernutzen oder endlos nutzlose Utility-Funktionen zu generieren. Laut einem von GitClear im Jahr 2024 veröffentlichten Analysebericht über 623 Millionen Commit-Zeilen stieg die Code-Duplizierung nach der Verbreitung von AI-Tools um 81 %, während der Anteil von Refactorings zum Bereinigen bestehenden Codes von 25 % auf unter 10 % einbrach. Aufgrund des Recency Bias im Kontextfenster weichen Agenten, um Risiken zu vermeiden, immer wieder auf die gängigsten Standardmuster aus ihren Trainingsdaten aus. Man muss Fehlfunktionen des Agenten unterdrücken, indem man die vom Team geteilten Architekturregeln und Synchronisationslogiken explizit in Kontextdateien festlegt.
1. Abweichungen von Regeln in Legacy-Codebases verhindern
Um die je nach Entwicklungstool unterschiedlichen Anweisungen zu steuern, sollte man als Single Source of Truth zuerst eine AGENTS.md im Projekt-Root anlegen. Tools wie Cursor und Claude Code lesen diese Regeln. Eine einzelne .cursorrules-Datei ist längst veraltet. Teilen Sie die Regeln stattdessen in MDC-Dateien im Verzeichnis .cursor/rules/ auf und rufen Sie in der Claude Code-Umgebung @AGENTS.md innerhalb von CLAUDE.md auf, um ein Verwässern der Regeln zu verhindern.
`yaml
description: Backend API and Domain Service architectural constraints
globs: apps/api//*.ts, services//*.ts
alwaysApply: false
Backend Architectural Constraints
Mandatory Patterns
- Use Canonical Response DTOs located in
apps/api/src/common/dto.
- All database mutations must go through the Unit of Work pattern defined in
services/shared/uow.
Explicit Negative Constraints
- NEVER create single-child base classes or single-implementation interfaces.
- NEVER write wrapper functions that merely pass arguments to lower-level services.
- NEVER implement strategy or factory patterns when standard conditional logic (if/switch) suffices.
- NEVER add defensive "just-in-case" try-catch blocks that return dummy fallback values without rethrowing.
`
Wenn die Gesamtlänge des Kontexts 800 Zeilen bzw. etwa 2.000 Token überschreitet, beginnt der Agent, die weiter oben festgelegten Einschränkungen zu ignorieren. In der AGENTS.md sollten nur absolut unverhandelbare Verbote verbleiben, während detaillierte Architektur-Entscheidungsdokumente (ADRs) in das Verzeichnis docs/context/ ausgelagert werden müssen.
Reihenfolge zur Anwendung von Einschränkungen
- Aufgabe: Wählen Sie 1 Kernmodul aus und legen Sie verbotene Muster sowie Ausnahmeregeln fest.
- Vorgehensweise:
- Erstellen Sie eine
AGENTS.md im Projekt-Root und tragen Sie Bedingungen wie das Verbot von Single-Implementation-Interfaces und defensiven Try-Catch-Blöcken ein.
- Konfigurieren Sie unter
.cursor/rules/backend-constraints.mdc die Zieldateipfade (globs) und die YAML Frontmatter-Einschränkungen.
- Fügen Sie die Syntax
@AGENTS.md in die CLAUDE.md ein, um die Umgebungen abzugleichen.
- Erwartetes Ergebnis: Die Erzeugung unnnötigen Boilerplate-Codes wird reduziert, wodurch sich der Aufwand für Codekorrekturen um etwa 4 Stunden pro Woche verringert.
2. Pipeline zur Verifizierung von Agenten-Code
Hinter dem plausibel wirkenden Code eines Agenten verbergen sich oft Fallstricke. Laut einer Studie von Veracode aus dem Jahr 2024 wurden in 45 % des von AI vorgeschlagenen Codes Sicherheitslücken auf OWASP Top 10-Niveau gefunden. Das Verstecken von Fehlern durch das laxe Umhüllen potenziell fehlerhafter Logik mit Try-Catch und dem anschließenden Werfen leerer Objekte oder Null-Werte kommt bei AI-generiertem Code 47 % häufiger vor als bei menschlichen Entwicklern. Auch das Vergessen von optimistischem Sperren bei Read-Modify-Write-Operationen oder das N+1-Problem durch wiederholte DB-Aufrufe innerhalb von Schleifen treten regelmäßig auf.
| Verifizierungsbereich |
Detaillierte Prüfpunkte |
Risikomuster & Agenten-Fallstricke |
Kriterien für Merge-Blockade |
| Sicherheitslücken |
Verwendung von Parameterized Queries, Mandantenisolierung, Prüfung nicht autorisierter Pakete |
SQL-Injection durch String-Verkettung, Aufruf unprüfter Pakete durch Halluzinationen |
Fehlende Eingabevalidierung, Blockade bei Hinzufügen externer Abhängigkeiten unklarer Herkunft |
| Performance-Engpässe |
ORM Lazy Loading, Verschachtelte Schleifen im Hot Path, DB-Indizes |
Abfrage einzelner Entitäten innerhalb von Schleifen, Filtern ganzer Tabellen im Speicher |
Blockade bei DB- und externen API-Aufrufen in Schleifen, Blockade bei fehlender Paginierung |
| Typsicherheit |
Strict Type-Prüfung, Boundary-Ausnahmebehandlung, Nebenläufigkeitssteuerung |
Exzessives as any, Fehlerversteckung durch leere Catch-Blöcke |
Blockade bei Existenz von any und unbedachtem as-Casting, Blockade bei Catch-Blöcken ohne Logging |
Testcode, den der Agent schreibt, beschränkt sich oft auf tautologische Tests, die den Implementierungscode lediglich eins-zu-eins kopieren. Solche Tests können reale fachliche Defekte niemals aufdecken. Reglementierungen wie "Wenn bereits eine Utility-Funktion existiert, muss die vom Agenten neu erstellte Utility gelöscht und der bestehende Code wiederverwendet werden" müssen streng durchgesetzt werden.
Manueller Verifizierungsprozess
- Aufgabe: Befüllen Sie Arbeitsvorlagen und CI-Gates mit einer Checkliste zur Verifizierung von Sicherheit, Performance und Typen.
- Vorgehensweise:
- Nehmen Sie Punkte wie Parameterized Queries, Vermeidung von N+1-Abfragen und das Verbot von
any in die PR-Vorlage auf.
- Binden Sie statische Analysetools in den Git Pre-Commit Hook ein, um Commits abzuweisen, wenn
as any oder ein await innerhalb einer Schleife erkannt wird.
- Ersetzen Sie beim Review vom Agenten eigenmächtig neu erstellte Utilities konsequent durch bestehende gemeinsame Module.
- Erwartetes Ergebnis: Verhindert, dass Mängel wie N+1-Abfragen oder Speicherlecks in die Produktion gelangen.
3. Zerlegtes Prompting zur Vermeidung von Denkblockaden
Wenn man komplexe Abrechnungslogiken oder Zustandsautomaten-basierte Bestellabwicklungen in einen einzigen Prompt zwängt, gerät der Agent schnell in eine Tool-Aufrufloop oder liefert nur oberflächlichen Hüllencode. Das liegt daran, dass sich die Token-Zuweisungsreihenfolge verheddert, wenn versucht wird, Schema-Design, API-Schnittstellen, Ausnahmebehandlung und Geschäftsregeln auf einmal zu verarbeiten.
`
[Schritt 1: Datenmodellierung] -> DB-Entitäten, Zod-Schemas erstellen
│
▼ (Ausgabeergebnis als Kontext übergeben)
[Schritt 2: Schnittstellendefinition] -> API-DTOs, Custom Errors, Servicesignaturen definieren
│
▼ (Ausgabeergebnisse aus Schritt 1+2 als Kontext übergeben)
[Schritt 3: Implementierung der Geschäftslogik] -> Transaktionen, Zustandsübergänge, Nebenläufigkeitssteuerung fertigstellen
`
Diesen Prozess manuell hin und her zu kopieren, ist ziemlich umständlich. Es ist besser, ein CLI-Automatisierungsskript (scripts/agent-decomposed-build.ts) zu schreiben, das die Ergebnisse der vorherigen Stufe direkt als Eingabekontext für die nächste Stufe verknüpft.
`typescript
import { execSync } from 'child_process';
import * as fs from 'fs';
interface TaskPipeline {
featureName: string;
stage1Prompt: string;
stage2Prompt: string;
stage3Prompt: string;
}
async function runDecomposedAgentPipeline(pipeline: TaskPipeline) {
console.log([Stage 1] Executing Data Modeling for ${pipeline.featureName}...);
const stage1Output = execSync(claude --print "${pipeline.stage1Prompt}").toString();
fs.writeFileSync(./tmp/${pipeline.featureName}_stage1.ts, stage1Output);
console.log([Stage 2] Executing Interface Definition...);
const stage2InputPrompt = ${pipeline.stage2Prompt}\n\nContext Models:\n${stage1Output};
const stage2Output = execSync(claude --print "${stage2InputPrompt}").toString();
fs.writeFileSync(./tmp/${pipeline.featureName}_stage2.ts, stage2Output);
console.log([Stage 3] Executing Business Logic Implementation...);
const stage3InputPrompt = ${pipeline.stage3Prompt}\n\nContext Models:\n${stage1Output}\n\nContext Contracts:\n${stage2Output};
const stage3Output = execSync(claude --print "${stage3InputPrompt}").toString();
fs.writeFileSync(./src/services/${pipeline.featureName}.service.ts, stage3Output);
console.log([Pipeline Complete] Business logic generated cleanly without cognitive stagnation.);
}
`
Aufbau des Skripts für zerlegtes Prompting
- Aufgabe: Etablieren Sie ein 3-stufiges Prompt-Template und stellen Sie ein CLI-Skript bereit, das dieses sequentiell ausführt.
- Vorgehensweise:
- Unterteilen Sie die Prompts in 3 Teile: Datenmodellierung, Schnittstellendefinition und Implementierung der Geschäftslogik.
- Schreiben Sie
scripts/agent-decomposed-build.ts, um die vorherigen Ausgaben so zu verbinden, dass sie in den Kontext des nächsten Prompts einfließen.
- Führen Sie dieses Skript aus, wenn Sie ein neues komplexes Modul erstellen.
- Erwartetes Ergebnis: Das Phänomen, dass der Agent keine Antwort findet und blockiert, verschwindet, und die Quote manueller Nachbearbeitung fällt von über 20 % auf unter 5 %.
4. Konfiguration zur Dokumentation von Entscheidungsgründen im Code
Je mehr man AI-Coding-Tools nutzt, desto mehr häuft sich "Write-Only-Code" an, dem die Begründung fehlt, warum er so geschrieben wurde. Code, in dem die Trade-Offs von Architekturentscheidungen nicht dokumentiert sind, wird später zu einer Last, die für Menschen schwer anzupassen ist. Man muss in der AGENTS.md erzwingen, dass der Agent beim Erstellen von Code TSDoc-Standardkommentare direkt im Quellcode hinterlässt.
`typescript
/**
- @description Processes deferred settlement payouts for multi-vendor orders.
- @why Uses pessimistic database locking on the Wallet entity instead of optimistic locking because payout calculation involves high-frequency concurrent balance updates.
- @tradeoff Slight P95 latency increase under high contention in exchange for 0% financial drift.
- @complexity Time: O(N log N) due to vendor sorting | Space: O(N) for batch processing buffer.
*/
export async function processDeferredSettlement(orderId: string): Promise {
if (account.hasOutstandingBalance()) {
this.applySettlementHold(account);
}
}
`
Um zu verhindern, dass die Agenteneinstellungen zwischen verschiedenen Entwicklern abweichen, sollte man AGENTS.md als Single Source of Truth verwalten und beim Commit ein Skript (tools/sync-agent-rules.ts) ausführen, das sie mit CLAUDE.md und .cursor/rules/global.mdc synchronisiert.
`typescript
import * as fs from 'fs';
import * as path from 'path';
const AGENTS_MD_PATH = path.join(__dirname, '../AGENTS.md');
const CLAUDE_MD_PATH = path.join(__dirname, '../CLAUDE.md');
const CURSOR_RULE_PATH = path.join(__dirname, '../.cursor/rules/global.mdc');
function syncRules() {
if (!fs.existsSync(AGENTS_MD_PATH)) {
console.error('Error: AGENTS.md does not exist.');
process.exit(1);
}
const baseRules = fs.readFileSync(AGENTS_MD_PATH, 'utf-8');
const claudeContent = # AUTOMATICALLY GENERATED FROM AGENTS.md - DO NOT EDIT DIRECTLY\n\n${baseRules};
fs.writeFileSync(CLAUDE_MD_PATH, claudeContent);
const mdcHeader = ---\ndescription: Global Agent Rule Sync\nglobs: **/*\nalwaysApply: true\n---\n\n;
fs.writeFileSync(CURSOR_RULE_PATH, ${mdcHeader}${baseRules});
console.log('Successfully synchronized AGENTS.md to CLAUDE.md and Cursor MDC rules.');
}
syncRules();
`