Nach dem book-library Tutorial des AI Unified Process hat es mich gepackt. Ich wollte den Ansatz von Grund auf an einem eigenen Beispiel ausprobieren. Also habe ich eine Fragestellung aus einem aktuellen Kundenprojekt genommen und auf das Nötigste reduziert. Daraus wurde eine kleine Fallverwaltung, in der Fälle mit Dokumenten, Beteiligten und Notizen geführt werden. Die Dokumente werden im Volltext indexiert und sind durchsuchbar.
Im Kern dreht sich alles um den Zugriffsschutz. Wer einen Fall sehen darf, entscheidet nicht der Java-Code, sondern PostgreSQL mit Row-Level Security. Dass ein aktueller Coding-Agent das korrekt umsetzen kann, habe ich nicht bezweifelt. Mich interessierte der Prozess. Wie fühlt es sich an, wenn alles mit Artefakten wie requirements.md und Use Cases beginnt? Wie gestaltet sich die Zusammenarbeit mit dem Agenten, und bleiben die Sessions kontrolliert und zielgerichtet? (Nein, hier kein Rust.)
Was entstanden ist
Das Ergebnis ist simple-case: 67 Commits, 13 Use Cases, gebaut mit Java 25, Spring Boot 4, Vaadin 25, jOOQ, PostgreSQL und Elasticsearch 8. Bis auf die vision.md und die architecture.md hat Claude Code jede Datei geschrieben, mit Opus 5.5 und AIUP 2.7.0 (Stand Oktober 2026). Von Hand habe ich keine Zeile geändert.
Die vision.md hat 49 Zeilen. Sie beschreibt das Problem: In Kanzleien sieht man bei einer Suche schnell Fälle, die einen nichts angehen, obwohl das Mandatsgeheimnis genau das verbietet. (Hoffentlich nicht in deiner.) Ausserdem nennt die Vision drei Zielgruppen: Anwalt, Assistenz und Admin. Die Liste der Nicht-Ziele ist länger als die der Ziele. Keine Honorarerfassung, keine Fristen, kein Fallstatus, kein Änderungsprotokoll. Die Vision wurde ein einziges Mal angepasst, beim letzten Use Case.
Dazu kam die architecture.md aus book-library, an das neue Projekt angepasst. Von dort stammt das Gerüst: Pakete nach Feature, in jedem genau zwei Unterpakete ui und domain, und ein Service nur dann, wenn er echte Logik enthält. Row-Level Security habe ich vorgegeben, ausgearbeitet hat sie der Agent. Die Anwendung verbindet sich mit einer Datenbankrolle, die Row-Level Security nicht umgehen kann, und ist nie Eigentümerin der Tabellen. Elasticsearch weiss nichts über Freigaben. Die Suche filtert auf die fall_ids, die PostgreSQL für den angemeldeten User liefert, und kontrolliert vor der Anzeige, dass jeder Treffer auf der Seite zu einem dieser Fälle gehört.
SimpleCase ist für rund 10'000 Fälle ausgelegt. Das erklärt einige Entscheide. Die Suche gibt Elasticsearch zum Beispiel bei jeder Anfrage die komplette Liste der sichtbaren Fälle mit. Bei 10'000 Fällen geht das gut, bei Millionen würde ich es anders lösen. Das als Hinweis, bevor mich jemand auf die fehlende Skalierung anspricht. ;)
Der Ablauf
Ich habe mich an den vorgegebenen Ablauf gehalten. Nach der vision.md kamen einmalig /requirements, /entity-model und /use-case-diagram. Danach ging es für jeden Use Case gleich weiter: /use-case-spec, /implement, /browserless-test, /playwright-test und zum Schluss /coverage-check. /flyway-migration lief nur, wenn ein Use Case das Schema änderte.
Zugegeben, bei diesem erfundenen Projekt habe ich die Spezifikationen und Coverage-Berichte nur überflogen. Wo etwas zu korrigieren war, habe ich es dem Agenten gesagt, und das war selten nötig.
In einem echten Projekt muss man sich genau hier die Zeit nehmen, die es braucht. Lücken in den Anforderungen kosten später unverhältnismässig viel Aufwand, genauso wie eine zu knappe oder zu ausführliche Spezifikation. Man kommt langsam vom direkten Weg ab und führt mit dem Agenten Diskussionen, die man nicht bräuchte. Und der Agent ist jederzeit bereit, mit einem ins Rabbit Hole zu steigen.
Kleine Beispiele dafür gibt es auch in SimpleCase. Der Use Case «Anmelden» hatte zuerst einen eigenen Ablauf, dass die Datenbank nicht erreichbar ist, obwohl die Architektur das an einer zentralen Stelle regelt. Der Ablauf flog noch vor der Umsetzung wieder raus. Eine Anforderung verlangte TLS und wurde erst am letzten Tag gestrichen, weil eine Demo-App keins braucht.
Was in der CLAUDE.md steht
Die CLAUDE.md von SimpleCase ist kurz. Sie legt die Sprache der Dokumente fest, verweist auf das Glossar und enthält eine Regel, die mir viel gebracht hat:
In Antworten der Session steht nach jeder ID von Use Case, Geschäftsregel, FR oder NFR die fachliche Bezeichnung in Klammern, zum Beispiel «UC-001 (Anmelden)» oder «NFR-017 (Datenbank nicht erreichbar)».
AIUP nummeriert alles: Use Cases, Geschäftsregeln, Abläufe, Anforderungen. Der Agent übernimmt die Nummern in seine Antworten, und ohne die Regel liest man Sätze wie «GR-004 widerspricht NFR-015». Bei jeder Rückfrage musste ich erst nachschlagen, worum es geht. Mit der Bezeichnung in Klammern verstehe ich eine Frage beim ersten Lesen und kann gleich entscheiden. In den Dokumenten selbst stehen die IDs weiterhin ohne Bezeichnung.
Zwei andere Regeln habe ich in den ersten 24 Stunden wieder gestrichen: «bei offenen Entscheidungen nachfragen» und «nach jeder Änderung den Spec-Lint laufen lassen». Beides regeln die AIUP-Skills schon selbst. Den Review der Spezifikationen starte ich seither mit /spec-review.
Wo es geklemmt hat
Es hat an einer Stelle geklemmt, und zwar nicht bei AIUP, sondern bei PostgreSQL. Die «Fallübersicht» war der erste Use Case, der alle sichtbaren Fälle zählt, sortiert und seitenweise anzeigt. Die Policy auf fall rief für jede Zeile fall_sichtbar(id) auf, und das bedeutete bis zu vier kleine Abfragen pro Fall. Laut Kommentar in der Migration dauerte eine Abfrage bei 10'000 Fällen so rund fünf Sekunden. Die Korrektur holt mit einer neuen Funktion alle freigegebenen Fälle in einer Abfrage und vergleicht jede Zeile nur noch mit dieser Liste:
CREATE POLICY fall_select ON fall FOR SELECT USING (
(SELECT app_ist_admin())
OR verantwortlicher_anwalt_id = (SELECT app_user_id())
OR id IN (SELECT freigegebene_fall_ids()));
Die Prüffunktionen laufen mit SECURITY DEFINER + BYPASSRLS, damit sich die Policies nicht gegenseitig endlos aufrufen. Schnell wird es durch das (SELECT …) um die Funktionsaufrufe. So wertet PostgreSQL jede Funktion einmal pro Abfrage aus statt einmal pro Zeile.
Die Tests
Ein kompletter ./mvnw verify führt 368 Tests aus und dauert 4 Minuten 47 Sekunden. 289 Tests laufen ohne Browser, darunter die Tests der Views und die ArchUnit-Regeln. 79 sind Playwright-Tests, die die Anwendung im Browser bedienen. Beide Arten laufen mit Testcontainers gegen echtes PostgreSQL und Elasticsearch. Zusammen sind das rund 10'500 Zeilen Testcode für 5'450 Zeilen Anwendungscode.
Fast jede Testmethode trägt eine Annotation @UseCase, die sie mit einem Use Case und seinen Geschäftsregeln verbindet:
@UseCase(id = "UC-002", scenario = "A1: Sitzung bereits abgelaufen")
So lässt sich für jede Geschäftsregel nachprüfen, welcher Test sie abdeckt. Die Coverage-Berichte haben bei drei Use Cases nachträglich Lücken gefunden, die der Agent danach geschlossen hat. Für die Freigabe verlangt eine Anforderung mindestens einen Test pro Rolle, Freigabeweg und Datenart. Ein weiterer Test führt jede Suchanfrage für einen User ohne Freigabe aus und erwartet null Treffer.
Damit dürfte SimpleCase die am genauesten spezifizierte und am gründlichsten getestete Fallverwaltung für Kanzleien sein, die je als Hobbyprojekt entstanden ist. Die Konkurrenz in dieser Kategorie ist überschaubar.
In Das Wissen kam vom Machen habe ich geschrieben, dass sich nicht alles aufschreiben lässt. Man kann aber prüfbar machen, was sich prüfen lässt. Genau das passiert hier. Eine Geschäftsregel ohne Test fällt beim /coverage-check auf und nicht erst, wenn jemand danach sucht.
Was ich mitnehme
Für die Entwicklung von Geschäftsanwendungen passt AIUP sehr gut. Es verlangt wenig, nur ausgerechnet das, was in Projekten am schwersten fällt. Anforderungen und Spezifikationen, sorgfältig und ohne Widersprüche. Den Rest übernimmt der Ablauf. Jeder Use Case durchläuft dieselben Schritte, und am Ende hängt an jeder Geschäftsregel ein Test.
Auch Zahlen in den Anforderungen lohnen sich. Die 10'000 Fälle standen dort, bevor es eine Zeile Code gab. Ich vermute, deshalb fiel das Problem mit den fünf Sekunden schon bei der Fallübersicht auf und nicht erst im Betrieb.
Die Sessions blieben bei der Sache, solange die Spezifikation klar war. Wo sie Lücken hatte, ergänzte der Agent sie nach dem Code, etwa um Leerzeichen, die bei Namen nicht zählen, oder um das Blättern hinter die letzte Seite der Suche. Manchmal fiel eine Lücke erst beim nächsten Use Case auf. Was der Admin bei Freigaben darf, war erst klar, als «Freigaben verwalten» spezifiziert wurde, und dafür mussten Regeln in «Fall öffnen» angepasst werden. Man übersieht etwas, merkt es später und bessert nach, wie in jedem Projekt.
Gebaut habe ich SimpleCase, weil ich zu AIUP aus eigener Erfahrung beraten will. Wer das Projekt selbst starten will, findet die Anleitung im README.
- Einen anderen Weg, einem Agenten Kontext über Sessions hinweg zu geben, habe ich in Ein Gedächtnis aus Tickets ausprobiert. Mit Beads hält der Agent fest, was er unterwegs entschieden hat. Bei AIUP schlägt er Ergänzungen zur Spezifikation vor, und ich entscheide, ob sie hineinkommen.