Was macht Constraint-Driven Development?
Überblick
Andere Fähigkeiten in diesem Paket beschreiben, wie "gut" aussieht. code-review-and-quality liefert fünf Achsen. test-driven-development liefert einen Zyklus. security-and-hardening liefert eine Bedrohungsliste. All das lebt in Prosa, die der Agent liest und möglicherweise nicht befolgt, und nichts davon überlebt das Ende der Sitzung.
Diese Fähigkeit produziert etwas anderes: ein schriftliches Protokoll der Qualitätslatte dieses Projekts mit Zahlen, das die Konversation überdauert und maschinell überprüft werden kann.
Der Grund ist wichtig. Als du den Code geschrieben hast, konntest du durch Lesen beurteilen, ob er gut war. Ein Agent schreibt an einem Nachmittag mehr, als du in dieser Woche lesen wirst, also wandert das Urteil aus deinem Kopf in Prüfungen, die im Loop laufen. Diese Prüfungen müssen existieren, sie brauchen Zahlen, die du tatsächlich gewählt hast, und sie müssen nahe genug an der Arbeit feuern, dass der Agent seine eigene Ausgabe korrigiert.
Spec-getriebene Entwicklung sagt, was gebaut werden soll. Test-getriebene Entwicklung beweist, dass es funktioniert. Constraint-getriebene Entwicklung definiert, was "gut genug zum Ausliefern" bedeutet, bevor jemand in einem Pull Request darüber streitet.
Wann anwenden
Wende diese Fähigkeit an, wenn:
- Ein Projekt oder eine bedeutende Funktion gestartet wird und keine Qualitätslatte festgelegt ist
- Der Benutzer bittet, "Constraints einzurichten", "Qualitätstore hinzuzufügen", "unsere Standards zu definieren" oder "den Agent davon abzuhalten, Schrott auszuliefern"
- Ein Agent produziert eine Menge, die niemand Zeile für Zeile liest
- CI hat zwar Prüfungen, aber niemand kann sagen, welche einen Merge blockieren und welche Dekoration sind
- Werte für Abdeckung, Leistung oder Barrierefreiheit pro PR diskutiert werden, anstatt einmal festgelegt zu werden
- Du kurz davor bist,
/build autooder einen anderen autonomen Loop auszuführen, und die einzige Hürde zwischen ihm und dem Hauptzweig eine Testsuite ist, die der Agent ebenfalls geschrieben hat
Wann NICHT anwenden:
- Das Projekt hat bereits eine
CONSTRAINTS.mdund der Benutzer ändert sie nicht – lies sie und befolge sie stattdessen - Einmal-Skripte, Spikes, Wegwerf-Prototypen
- Der Benutzer möchte jetzt eine Code-Review (
code-review-and-quality) oder eine CI-Pipeline (ci-cd-and-automation) - Code vor dem Product-Market-Fit mit einer erwarteten Lebensdauer von zwei Wochen – der Floor darunter lohnt sich trotzdem, der Rest nicht
Constraints laden
Das Interview benötigt einen live-Benutzer. Führe es nicht in nicht-interaktiven Kontexten aus (CI, /loop, autonome Läufe). Wenn Constraints fehlen und du in einem solchen Kontext bist, wende den Floor unten an, notiere, dass du es getan hast, und markiere den Rest für einen Menschen.
Der Prozess
Schritt 1: Erkennen, bevor du fragst
Frage nie, was du lesen kannst. Sammle vor der ersten Frage:
| Was | Wo suchen |
|---|---|
| Sprache und Stack | package.json, pyproject.toml, go.mod, Cargo.toml |
| Test-Runner | dev-Abhängigkeiten, test-Skript, vorhandene Testdateien |
| Vorhandene Linter | eslint.config.*, biome.json, .ruff.toml |
| Aktuelle Abdeckung | coverage/-Ausgabe oder einmal die Suite ausführen |
| CI | .github/workflows/, .gitlab-ci.yml |
| Agent-Umgebung | .claude/, .codex/, AGENTS.md |
Berichte das Gefundene in zwei Zeilen, dann frage nur, was übrig bleibt.
Schritt 2: Vier Fragen, jede mit einem Standardwert
Befolge die Disziplin "eine Frage nach der anderen" aus interview-me, mit einer Änderung: Jede Frage hier hat einen Standardwert, also ist "Ich weiß nicht" eine vollständige Antwort, die trotzdem eine funktionierende Konfiguration produziert.
F1: Über den Floor hinaus, welche dieser Dimensionen möchtest du erzwingen?
(a) Testabdeckung auf neuem Code
(b) Sicherheitsscans
(c) Performance-Budgets
(d) Barrierefreiheit
(e) Architekturgrenzen
VERMUTUNG: (a) und (b) – du hast bereits einen Test-Runner und verarbeitest Benutzereingaben.
STANDARD bei Unsicherheit: (a) und (b).
Sage, was jede Wahl kostet: (c) und (d) benötigen eine laufende URL, (e) benötigt eine Regelsammlung.
F2: Wenn ein Check fehlschlägt, während der Agent mitten in der Aufgabe steckt, soll er blocken oder warnen?
VERMUTUNG: Blocken. Du betreibst Agents unbeaufsichtigt, und eine Warnung, die niemand liest, ist eine Warnung für die Tonne.
STANDARD bei Unsicherheit: Beim Floor blocken, bei allem anderen für die ersten zwei Wochen warnen.
F3: Hast du Zielzahlen im Kopf, oder soll ich messen, wo du heute stehst, und diese Linie halten?
VERMUTUNG: Messen. Die meisten Teams haben keine Zahl, und eine erfundene wird ignoriert.
STANDARD bei Unsicherheit: Messen und halten. Siehe "Ratchets" unten.
F4: Was ist der langsamste Check, den du tolerierst, bevor der Agent die Arbeit zurückgibt?
VERMUTUNG: Etwa 90 Sekunden. Länger, und du wirst ihn nicht mehr ausführen.
STANDARD bei Unsicherheit: 90 Sekunden am Ende der Aufgabe, unbegrenzt in CI.
Höre bei vier auf. Eine Aufnahme mit zwölf Fragen produziert eine Konfiguration, die niemand versteht, und einen Benutzer, der den Start bereut.
Schritt 3: CONSTRAINTS.md schreiben
Eine Datei im Repository-Stamm. Jeder Agent auf jeder Umgebung kann sie lesen, und eine Änderung daran taucht im Review auf, wo sie hingehört.
# Constraints
Zuletzt überprüft: 2026-08-08 von @mukhon-core
## Floor (immer erzwungen, kein Setup erforderlich)
- Keine neuen Suppression-Kommentare: `@ts-ignore`, `eslint-disable`, `# noqa`, `# type: ignore`
- Keine unvollständigen Stubs: `throw new Error("Not implemented")`, leere `catch {}`
- Keine übersprungenen oder gelöschten Tests ohne Grund in der Commit-Nachricht
- Keine Geheimnisse im Quellcode
- Diese Datei wird nicht geschwächt, um eine Änderung durchzubringen
## Mit Zahlen erzwungen
| Dimension | Regel | Geprüft durch | Läuft bei |
|-----------|-------|-------------|----------|
| Typen | Null Typfehler | `tsc --noEmit` | jeder Edit |
| Lint | Null Fehler aus unserer Konfiguration | `biome check` | jeder Edit |
| Geheimnisse | Keine Geheimnisse im Quellcode | `gitleaks detect --redact` | jeder Edit |
| Abdeckung | Geänderte Zeilen ≥ 80% abgedeckt | `vitest run --coverage` + git diff | Aufgabenende, CI |
| Sicherheit: Code | Keine hohen Befunde | `semgrep scan --config p/default` | CI |
| Sicherheit: Abhängigkeiten | Nichts auf hoch oder höher | `osv-scanner scan source -r .` | CI |
| Barrierefreiheit | Null kritische oder schwerwiegende Fehler | `axe $PREVIEW_URL --tags wcag2a,wcag2aa,wcag21aa` | Preview-Deployment |
| Leistung | LCP ≤ 2500ms, CLS ≤ 0.1 | `lighthouse $PREVIEW_URL --output=json` | Preview-Deployment |
Jede Zeile benennt den Befehl, der das Urteil produziert. Eine Dimension mit einer Zahl und keinem Befehl in dieser Spalte ist eine Aspiration, keine Einschränkung.
## Gemessen, noch nicht erzwungen
| Metrik | Heute | Richtung |
|--------|-------|---------|
| Projektabdeckung | 62,4% | darf nicht fallen |
| Bundle-Größe (main) | 184 kB | darf nicht wachsen |
## Ausnahmen
| ID | Regel | Pfad | Grund | Eigentümer | Läuft ab |
|----|-------|------|-------|-----------|---------|
| W1 | `no-explicit-any` | `src/legacy/**` | Umschreibung in ENG-441 erfasst | @mukhon-core | 2026-11-01 |
Füge dann eine Zeile in AGENTS.md und CLAUDE.md hinzu: Lies CONSTRAINTS.md, bevor du Code schreibst. Schwäche sie nicht, um eine Änderung durchzubringen.
Schritt 4: Installieren, was jede Dimension braucht
Eine Dimension auszuwählen bedeutet, etwas zu installieren. Lasse den Benutzer nicht mit einer Zahl und keinem Mechanismus zurück, und erfinde deinen eigenen Prüfer nicht, wenn ein De-facto-Standard existiert – diese Tools sind aufgeführt, weil ihre Regelformate und Schwellenwerte das Ziel des restlichen Ökosystems sind, sodass die vorhandene Konfiguration des Teams weiterhin funktioniert.
| Dimension | Tool | Installation | Ausführung | Sperre bei |
|---|---|---|---|---|
| Typen (TS) | tsc | bereits vorhanden | tsc --noEmit | jeder Fehler |
| Typen (Python) | mypy | pip install mypy | mypy . | jeder Fehler |
| Lint | deine vorhandene Konfiguration | bereits vorhanden | eslint . / biome check / ruff check | jeder Fehler |
| Abdeckung (JS) | dein Test-Runner | bereits vorhanden | vitest run --coverage (oder jest --coverage) | Abdeckung geänderter Zeilen |
| Abdeckung (Python) | pytest-cov | pip install pytest-cov | pytest --cov --cov-report=lcov | gleich |
| Sicherheit: Code | Semgrep | pipx install semgrep | semgrep scan --config p/default --config p/owasp-top-ten | jeder hohe Befund |
| Sicherheit: Geheimnisse | gitleaks | brew install gitleaks | gitleaks detect --redact --no-banner | jeder Befund |
| Sicherheit: Abhängigkeiten | osv-scanner | brew install osv-scanner | osv-scanner scan source -r . | hoch oder höher |
| Leistung: Seite | Lighthouse | npm i -D lighthouse | lighthouse $URL --output=json --quiet | LCP, CLS, Performance-Score |
| Leistung: Bundle | size-limit | npm i -D size-limit | size-limit --json | pro Eintrag Byte-Budget |
| Barrierefreiheit | axe-core | npm i -D @axe-core/cli | axe $URL --tags wcag2a,wcag2aa,wcag21aa | null kritisch oder schwerwiegend |
| Architektur | dependency-cruiser | npm i -D dependency-cruiser | depcruise --validate src | jede Verletzung |
| Assertionsqualität | Stryker | npm i -D @stryker-mutator/core | stryker run --mutate <geänderte Dateien> | Mutationsscore |
Fünf Dinge, die dir schaden werden, wenn du sie überspringst:
--redactbei gitleaks ist nicht optional. Ohne den Parameter landet der gefundene Schlüssel im Transkript des Agents, was bedeutet, dass ein geleakter Schlüssel in einem Log, einer Zusammenfassung oder einer Commit-Nachricht endet. Melde die Regel und den Ort, niemals den Wert.- Lighthouse und axe benötigen eine URL. Sie funktionieren nur gegen eine laufende Anwendung, also gehören sie in die Laufzeitstufe gegen ein Preview-Deployment oder einen lokalen Server, den du zuerst startest. Wenn das Projekt keine URL hat, die es zu treffen gilt – ein CLI-Tool, eine Bibliothek, eine Desktop-App – sage das und lasse die Dimension fallen, anstatt eine Prüfung zu erfinden, die nicht laufen kann.
- Beschränke die teuren Prüfungen auf den Diff.
stryker run --mutateauf dem gesamten Repository dauert Stunden und wird abgeschaltet; auf den Dateien, die eine Änderung berührt hat, dauert es unter einer Minute. Gleiches gilt für Semgrep, das eine Pfadliste akzeptiert. - Abdeckung benötigt keinen zweiten Testlauf. Lese den lcov, den deine Suite bereits schreibt, und schneide ihn mit
git diff. Die Suite zweimal auszuführen, um eine Zahl zu erhalten, ist der schnellste Weg, Leute dazu zu bringen, dies zu hassen. - Semgreps Registry-Regeln sind kostenlos zu verwenden; überprüfe die Lizenz, bevor du sie weiterverteilst.
opengrepist ein Drop-in-Fork mit demselben Regelformat und JSON-Ausgabe, falls das für dein Rechts-Team wichtig ist.
Füge jedes Tool in das projekteigene Skript ein, damit es ohne Agent reproduzierbar ist:
{
"scripts": {
"check:fast": "tsc --noEmit && eslint . && gitleaks detect --redact --no-banner",
"check:task": "npm run check:fast && vitest run --coverage",
"check:full": "npm run check:task && semgrep scan --config p/default && osv-scanner scan source -r ."
}
}
Dieses Mapping ist wichtiger als die Tools. check:fast läuft nach einem Edit, check:task wenn der Agent denkt, er sei fertig, check:full in CI.
Die Befehle leben jetzt an zwei Orten – der Spalte Geprüft durch in CONSTRAINTS.md und diesen Skripten. CONSTRAINTS.md ist die kanonische Quelle: sie führt den Grund neben jedem Befehl auf und taucht im Review auf. Die Skripte sind Komfort-Wrapper, die ihn spiegeln müssen, keine zweite Quelle der Wahrheit; wenn sie abweichen, gewinnt die Datei.
Schritt 5: An den Lebenszyklus anbinden
Der größte Fehler ist, alles überall laufen zu lassen. Ein Check, der den Agent aufhält, wird abgeschaltet, und ein Tor, das die Leute abgeschaltet haben, ist schlimmer als kein Tor, weil die Latte immer noch aussieht, als existiere sie.
| Phase | Befehl | Was läuft | Budget |
|---|---|---|---|
| BUILD | /build | Typen, Lint, Geheimnisse, der Floor | unter 5s, nur geänderte Datei |
| VERIFY | /test | Zugehörige Tests, Abdeckung auf geänderten Zeilen | unter 90s |
| REVIEW | /review | Alles, plus die folgenden Wächter | Minuten |
| SHIP | /ship | Richtungsprüfungen, keine Regressionen | CI |
Zwei Regeln, die dies erträglich halten:
- Auf den Diff beschränken. Überprüfe die Zeilen, die diese Änderung berührt hat, nicht das gesamte Repository. Die Abdeckung geänderter Zeilen ist eine Zahl, die der Agent bewegen kann; die Projektabdeckung ist eine, die er geerbt hat.
- Die Kosten bestimmen den Ort. Alles über ein paar Sekunden wandert aus dem Edit-Loop hinaus. Mutationstests auf einem gesamten Repository dauern Stunden; auf den Dateien, die eine Änderung berührt hat, dauern sie unter einer Minute – das ist der Unterschied zwischen einem Check, den Leute ausführen, und einem, den sie nicht ausführen.
Schritt 6: Die Latte selbst bewachen
Jemand wird darauf hinweisen, dass, wenn der Agent den Code und die Prüfungen schreibt, die Prüfungen nichts beweisen. Halb richtig, und es lohnt sich, dagegen zu konstruieren.
Agenten zimmern keine cleveren Schlupflöcher. Sie treffen auf einen roten Check und nehmen den billigsten Weg zu Grün. Achte auf diese fünf Bewegungen im Diff, beim Review:
- Der Schwellenwert hat sich geändert. Ein Budget gesenkt, eine Schweregrad gesenkt, ein Check aus der schnellen Stufe entfernt. Vergleiche
CONSTRAINTS.mdmit ihrem Zustand beim Abzweigpunkt. - Ein Test wurde einfacher.
.skiphinzugefügt, eine Testdatei gelöscht, Assertions aus Tests entfernt, die geblieben sind. - Ein Prüfer wurde stummgeschaltet. Neue
@ts-ignoreodereslint-disable. Vier Suppressionen verdienen besondere Aufmerksamkeit, weil sie einen Check ausschalten, auf den du dich verlässt:istanbul ignoreentfernt Code aus der Abdeckung anstatt ihn zu testen,Stryker disableversteckt einen überlebenden Mutanten,nosemgrepundgitleaks:allowtun dasselbe für Sicherheitsbefunde. - Arbeit ist unvollendet. Ein Stub, der einen Fehler wirft, ein leeres
catch, das einen Fehler in Schweigen verwandelt, einTODOan der Stelle, wo die Implementierung sein sollte. - Eine Ausnahme ist aufgetaucht. Eine neue Zeile in der Ausnahmetabelle, die niemand besprochen hat.
Nichts davon benötigt Werkzeuge jenseits von git diff. Die Latte zu verschärfen sollte leise sein; sie zu lockern sollte laut sein.
Im Gegensatz zu den nummerierten Dimensionen hat der Floor kein De-facto-eigenes Tool, daher neigt ein Agent, der gebeten wird, ihn durchzusetzen, dazu, einen Prüfer von Grund auf zu schreiben, und zwei Agenten schreiben zwei verschiedene. Eine Referenzimplementierung dieser fünf Prüfungen wird mit dieser Fähigkeit in references/floor-guard.md ausgeliefert (diff-begrenzt, Ausgang 0/1/2, Muster an das jeweilige Ökosystem anpassbar). Passe diese an, anstatt sie neu zu erfinden, aus demselben Grund, aus dem jede Dimension ein De-facto-Tool nennt: damit der Mechanismus über alle Läufe und Stacks hinweg derselbe ist.
Nicht alle Prüfungen sind gleichermaßen zirkulär. Bewerte sie nach einer Frage: Kann der Agent diese Prüfung bestehen, indem er Code schreibt, der nicht funktioniert?
- Extern – axe-core kodiert WCAG,
osv-scannerliest eine Schwachstellendatenbank, Lighthouse misst einen echten Browser. Der Agent kann mit diesen nicht argumentieren. - Projekt – deine Lint-Regeln, deine Schichtgrenzen. Ein Mensch besitzt die Datei.
- Suite – deine eigenen Tests. Am nützlichsten, und die einzige wirklich zirkuläre