Niemal wszystkie 1562 linie testów TopicReview napisał Claude. Ja robię tylko review — ponad dwa tygodnie na produkcji, wszystkie kolejne commity dodają testy, żaden ich nie przepisuje. Ten wpis traktuje o tym, dlaczego testy to idealny cel do delegowania, na co patrzeć i co pomijać w review (łącznie z realnie brakującym edge case w specu) i o domyślnej konfiguracji tego podziału.
Poprzedni wpis zamknęliśmy zdaniem „119 speców na zielono" dla TopicReview. Prawdziwe następne pytanie: kto napisał te testy?
Odpowiedź: Claude napisał niemal wszystkie 1562 linie kodu testów. Ja tylko robię review. Ponad dwa tygodnie na produkcji, a wzorzec utrzymania tych 1562 linii to tylko dodawanie nowych testów, nigdy przepisywanie starych.
Ten wpis dotyczy tego, dlaczego testy są najlepszym kandydatem do delegowania Claude'owi, na co patrzeć i co pomijać w review, i jak daleko ten podział faktycznie działa.
Testy TopicReview są rozrzucone w 7 plikach:
spec/services/topic_review_service_spec.rb 760 linii (88 testów)
spec/requests/topic_reviews_spec.rb 281 linii (32 testy)
spec/requests/review_appeals_spec.rb 152 linie (16 testów)
spec/requests/review_votes_spec.rb 127 linii
spec/policies/topic_review_policy_spec.rb 109 linii
spec/jobs/close_topic_review_job_spec.rb 71 linii (7 testów)
spec/models/topic_review_spec.rb 62 linie
───────────────────────────────────────────
1 562 linie
Cztery typy testów objęte: service (logika biznesowa), request (controller + integracja), policy (autoryzacja Pundit), job (zadania planowe).
Początkowy commit d162f1e nosi Co-Authored-By: Claude Sonnet 4.6 i ląduje z 1100+ tych linii za jednym zamachem. Każdy kolejny commit do speców to „Add test for..."—ani jeden refactor ani 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
Łatanie dziur, nie przerabianie. Ten szczegół wraca później.
Cztery twarde powody:
1. Wejścia i wyjścia są jawne. Test to w istocie „dany ten stan → oczekuj tego zachowania". To mocna strona Claude'a: przetłumaczyć specyfikację na assercję. Kod biznesowy czasem wymaga kompromisów; testy prawie nigdy.
2. Mechaniczne × duża ilość. Pojedynczy describe .open! musi pokryć „są uprawnieni przysięgli / brak / brak topic / aktywny review już istnieje"—cztery context, 2–5 it w każdym. Człowiek zaczyna ucinać rogi przy trzecim context. Claude pisze 88. it z tą samą starannością co pierwszy.
3. Bardzo krótka pętla feedbacku. Piszesz test, odpalasz rspec, w sekundach wiesz czy przechodzi. Kod biznesowy wymaga dni prawdziwego użycia, żeby problemy wypłynęły. Krótka pętla = każdy błąd Claude'a łapie rspec na miejscu—nie musisz pilnować.
4. Naturalnie równoległe. Bloki it są niezależne, bez ukrytych sprzężeń, skalują się trywialnie. Wygenerowanie dziesiątek izolowanych testów od razu to dokładnie to, w czym Claude jest dobry.
To punkt zwrotny całego podziału.
Ignorować:
Patrzeć:
Ostatni punkt to prawdziwa wartość review. Claude pokrywa testy „które mu przychodzą do głowy", ale te, które mu nie przychodzą, same się nie napiszą. Dokładnie tutaj wpisuje się ludzkie review—iść od tyłu od reguł biznesowych do brakującego pokrycia.
Otwórz początek describe ".open!" w spec/services/topic_review_service_spec.rb:
describe ".open!" do
context "when there are eligible jurors" do
# status review ok / post under_review / assignments utworzone / author powiadomiony / jurors powiadomieni / brak podwójnego open
end
context "when there are no eligible jurors" do
# review utworzone ale assignments nie
end
context "when post has no topic" do
# zwraca nil
end
end
Wygląda kompleksowo. Ale prawdziwa reguła eligible_jurors w modelu wyłącza trzy grupy:
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
Teraz spójrz na testy—który test twierdzi, że „autor posta nigdy nie jest wybierany do ławy"?
Przeszukaj service_spec.rb i model_spec.rb: nic. model_spec.rb testuje tylko kilka przypadków scope'a pending_vote_by; eligible_jurors bezpośrednio nie obejmuje. W service_spec.rb jest tylko komentarz: # Jurors must NOT be the post author—to w setupie, nie assercja.
Oto co review może wyłapać: trzy reguły wykluczenia (autor / zgłaszający / już-głosował-w-initial), żadna nie chroniona testem. Jeśli ktoś później refaktoryzuje eligible_jurors i przypadkiem zrzuci post.user_id z listy wykluczeń, wszystkie istniejące testy przejdą—a produkcja cicho pozwoli autorom zasiąść we własnej ławie.
Claude się nie mylił—przetestował to, o co go poproszono. Po prostu sam nie zapytał: „czy każda z tych trzech reguł potrzebuje pokrycia testem?" To pytanie—od reguł do pokrycia wstecznie—jest pracą review.
(Uczciwie: sam to przeoczyłem w początkowym review. Zauważyłem dopiero przy drugim audycie pisząc ten wpis. Więc review też nie jest one-shot—ale wciąż 10× lepsze niż brak review.)
Gdyby „Claude pisze + człowiek robi review" było idealne, po początkowym commicie nie byłoby nowych commitów testowych. Rzeczywistość jest ciekawsza—łatanie dziur bez przepisywania:
00393fc Add test for finalize! with zero votes (expired review)
3f53304 Add test for finalize! with legacy votes missing reasoning
Pierwszy to regression test po bugu—e8cb2db Default to keep verdict when review expires with zero votes to fix, 00393fc to parujący test. Ten sam wzorzec dla drugiego, w ślad za abaa22e Fix CloseTopicReviewJob failing due to reasoning validation on old votes.
Te dwa commity dowodzą dwóch rzeczy naraz:
„Wystarczająco dobrze + da się dalej łatać" to znacznie bardziej realistyczna poprzeczka niż „idealnie". Gonienie za idealnym review to właśnie to, co powstrzymuje cię przed oddaniem testów Claude'owi. Akceptacja „wystarczająco dobrze" uruchamia podział.
Nie każdy test nadaje się na pełny handoff:
W tych przypadkach prowadzi człowiek, a Claude asystuje.
Zamienić ten podział w wykonywalny default:
Programista ma mniej zasobu mentalnego na czytanie testów niż na czytanie kodu. Testy są repetytywne, mechaniczne, wyczerpujące i konieczne. Wszystko to opisuje dokładnie mocną stronę Claude'a—nie nudzi się, nie męczy, nie ścina rogów przy 50. it.
Twoja praca to nie „pisanie testów"—to „upewnienie się, że każda reguła biznesowa jest pokryta testem". Jedno to implementacja, drugie to osąd. Osąd zostaje przy tobie; implementacja idzie do Claude'a.
119 speców / 1562 linie wypuszczone w jednym commicie i żyjące ponad dwa tygodnie bez rework—to nie dlatego, że lepiej piszę testy, tylko dlatego, że wcale żadnego nie napisałem. Robię jedną rzecz, której Claude nie robi: decyduję, które reguły biznesowe zasługują na ochronę.