Fast alle 1.562 Zeilen der TopicReview-Tests wurden von Claude geschrieben. Ich mache nur das Review — über zwei Wochen im Produktivbetrieb, alle Folge-Commits fügen Tests hinzu, keiner schreibt sie neu. Dieser Beitrag handelt davon, warum Tests das ideale Delegationsziel sind, worauf man beim Review schauen und was man ignorieren sollte (einschließlich eines tatsächlich fehlenden Edge Case im Spec), und der Standardkonfiguration dieser Aufteilung.
Der letzte Beitrag endete mit „119 Specs grün" für TopicReview. Die eigentliche Folgefrage: Wer hat diese Tests geschrieben?
Antwort: Claude hat nahezu alle 1.562 Zeilen Testcode geschrieben. Ich mache nur das Review. Seit über zwei Wochen in Produktion, und das Wartungsmuster dieser 1.562 Zeilen lautet nur neue Tests hinzufügen, alte niemals umschreiben.
Dieser Beitrag handelt davon, warum Tests der beste Delegationskandidat für Claude sind, worauf beim Review zu achten ist und was man ignorieren sollte, und wie weit diese Aufteilung in der Praxis trägt.
Die TopicReview-Tests verteilen sich auf 7 Dateien:
spec/services/topic_review_service_spec.rb 760 Zeilen (88 Tests)
spec/requests/topic_reviews_spec.rb 281 Zeilen (32 Tests)
spec/requests/review_appeals_spec.rb 152 Zeilen (16 Tests)
spec/requests/review_votes_spec.rb 127 Zeilen
spec/policies/topic_review_policy_spec.rb 109 Zeilen
spec/jobs/close_topic_review_job_spec.rb 71 Zeilen (7 Tests)
spec/models/topic_review_spec.rb 62 Zeilen
───────────────────────────────────────────
1.562 Zeilen
Vier Testarten abgedeckt: service (Geschäftslogik), request (Controller + Integration), policy (Pundit-Autorisierung), job (geplante Tasks).
Der initiale Commit d162f1e trägt Co-Authored-By: Claude Sonnet 4.6 und landet 1.100+ dieser Zeilen auf einen Schlag. Jeder nachfolgende Spec-Commit ist „Add test for..."—kein einziger refactor oder rewrite:
00393fc Add test for finalize! with zero votes (expired review)
3f53304 Add test for finalize! with legacy votes missing reasoning
3b185da Update specs to use PROVISIONAL_PENALTY constant
Lücken stopfen, nicht neu machen. Dieses Detail kommt später wieder.
Vier harte Gründe:
1. Eingaben und Ausgaben sind explizit. Ein Test ist „gegeben dieser Zustand → erwarte dieses Verhalten". Das ist Claudes stärkste Übersetzungsaufgabe: Spec in Assertion verwandeln. Geschäftslogik verlangt manchmal Abwägungen; Tests fast nie.
2. Mechanisch × große Menge. Ein einziges describe .open! muss „hat geeignete Jurymitglieder / hat keine / kein Topic / bereits aktives Review" abdecken—vier Contexts mit je 2–5 it-Blöcken. Menschen beginnen beim dritten Context, Ecken abzuschneiden. Claude schreibt das 88. it mit derselben Sorgfalt wie das erste.
3. Extrem kurze Rückkopplung. Test schreiben, rspec laufen lassen, in Sekunden wissen, ob er grün ist. Geschäftscode braucht Tage Realbetrieb, bis Probleme auftauchen. Kurze Schleife = jeder Claude-Fehler wird sofort von rspec gefangen—du musst nicht babysitten.
4. Natürlicher Parallelismus. it-Blöcke sind unabhängig, keine versteckten Kopplungen, trivial skalierbar. Dutzende isolierte Tests auf einmal zu erzeugen, ist genau Claudes Stärke.
Das ist der Angelpunkt der gesamten Aufteilung.
Ignorieren:
Anschauen:
Der letzte Punkt ist der eigentliche Wert des Reviews. Claude deckt die Tests ab, „die ihm einfallen", aber die, die ihm nicht einfallen, entstehen nicht von selbst. Genau dort setzt menschliches Review an—rückwärts von den Geschäftsregeln zur fehlenden Abdeckung laufen.
Öffne den Anfang des describe ".open!" in spec/services/topic_review_service_spec.rb:
describe ".open!" do
context "when there are eligible jurors" do
# Review-Status ok / post under_review / assignments erstellt / author benachrichtigt / jurors benachrichtigt / kein Doppel-Open
end
context "when there are no eligible jurors" do
# Review wird erstellt, aber keine Assignments
end
context "when post has no topic" do
# gibt nil zurück
end
end
Sieht umfassend aus. Aber die echte Regel von eligible_jurors im Model schließt drei Gruppen aus:
def eligible_jurors
excluded_ids = [ post.user_id ] + post.reports.pluck(:user_id) + review_votes.where(stage: :initial).pluck(:user_id)
User.jurors_and_judges.where.not(id: excluded_ids.uniq)
end
Schau dir jetzt die Tests an—welcher Test behauptet, dass „der Autor des Posts nie als Juror ausgewählt wird"?
Such in service_spec.rb und model_spec.rb: nichts. model_spec.rb testet nur ein paar Fälle des pending_vote_by-Scopes; eligible_jurors selbst ist nicht abgedeckt. In service_spec.rb steht nur ein Kommentar: # Jurors must NOT be the post author—das steht im Setup, ist keine Assertion.
Genau das fängt das Review: drei Ausschlussregeln (Autor / Melder / hat-im-Initial-schon-gewählt), keine davon ist durch einen Test abgesichert. Refaktoriert später jemand eligible_jurors und fällt post.user_id aus der Ausschlussliste, laufen alle bestehenden Tests grün—und in Produktion setzen Autoren plötzlich in ihrer eigenen Jury.
Claude lag nicht falsch—er hat getestet, was verlangt war. Er hat nur nicht von selbst gefragt: „brauchen diese drei Regeln jeweils Testabdeckung?" Diese Frage—von Regeln zur Abdeckung rückwärts—ist Sache des Reviews.
(Ehrlich: ich habe das beim ersten Review auch übersehen. Ich habe es erst bei einem zweiten Audit beim Schreiben dieses Posts bemerkt. Also ist Review auch kein One-Shot—aber immer noch 10× besser als kein Review.)
Wäre „Claude schreibt + Mensch reviewt" perfekt, gäbe es nach dem Initial-Commit keine neuen Test-Commits. Was tatsächlich passierte, ist interessanter—Lücken füllen ohne Neu-Schreiben:
00393fc Add test for finalize! with zero votes (expired review)
3f53304 Add test for finalize! with legacy votes missing reasoning
Der erste ist ein Regressionstest nach einem Bug—e8cb2db Default to keep verdict when review expires with zero votes war der Fix, 00393fc der zugehörige Test. Dasselbe Muster für den zweiten, als Folge von abaa22e Fix CloseTopicReviewJob failing due to reasoning validation on old votes.
Diese zwei Commits belegen zwei Dinge gleichzeitig:
„Gut genug + kontinuierlich nachbesserbar" ist eine viel realistischere Messlatte als „perfekt". Dem perfekten Review hinterherzurennen ist genau das, was dich davon abhält, Tests an Claude zu übergeben. „Gut genug" zu akzeptieren bringt die Aufteilung zum Laufen.
Nicht jeder Test eignet sich für vollständigen Handoff:
Dort führt der Mensch und Claude assistiert.
Diese Aufteilung in einen ausführbaren Default gießen:
Programmierer haben fürs Lesen von Tests weniger mentale Kraft als fürs Lesen von Code. Tests sind repetitiv, mechanisch, zehrend und notwendig. Das alles beschreibt genau Claudes Stärke—er langweilt sich nicht, ermüdet nicht, schneidet beim 50. it keine Ecken.
Deine Aufgabe ist nicht „Tests schreiben"—sondern „sicherstellen, dass jede Geschäftsregel durch einen Test gedeckt ist". Das eine ist Implementierung, das andere Urteil. Urteil bleibt bei dir; Implementierung geht an Claude.
119 Specs / 1.562 Zeilen in einem Commit geliefert und über zwei Wochen ohne Rework—das liegt nicht daran, dass ich bessere Tests schreibe, sondern daran, dass ich überhaupt keine geschrieben habe. Ich mache nur eine Sache, die Claude nicht tut: entscheiden, welche Geschäftsregeln geschützt werden müssen.