Quasi tutte le 1.562 righe di test di TopicReview le ha scritte Claude. Io faccio solo la review — oltre due settimane in produzione, tutti i commit successivi aggiungono test, nessuno li riscrive. Questo post tratta del perché i test sono il bersaglio ideale da delegare, cosa guardare e cosa ignorare in review (incluso un edge case realmente mancante nello spec), e la configurazione di default di questa ripartizione.
Il post precedente chiudeva con «119 spec in verde» per TopicReview. La vera domanda successiva: chi ha scritto quei test?
Risposta: Claude ha scritto quasi tutte le 1.562 righe di codice di test. Io faccio solo review. Oltre due settimane in produzione, e il pattern di manutenzione di quelle 1.562 righe è solo aggiungere nuovi test, mai riscrivere i vecchi.
Questo post parla del perché i test sono il miglior candidato da delegare a Claude, cosa guardare e cosa ignorare in review, e fino a dove regge questa ripartizione nella pratica.
I test di TopicReview sono distribuiti su 7 file:
spec/services/topic_review_service_spec.rb 760 righe (88 test)
spec/requests/topic_reviews_spec.rb 281 righe (32 test)
spec/requests/review_appeals_spec.rb 152 righe (16 test)
spec/requests/review_votes_spec.rb 127 righe
spec/policies/topic_review_policy_spec.rb 109 righe
spec/jobs/close_topic_review_job_spec.rb 71 righe (7 test)
spec/models/topic_review_spec.rb 62 righe
───────────────────────────────────────────
1.562 righe
Quattro tipi di test coperti: service (logica di business), request (controller + integrazione), policy (autorizzazione Pundit), job (task pianificati).
Il commit iniziale d162f1e porta Co-Authored-By: Claude Sonnet 4.6 e atterra con 1.100+ di quelle righe in un colpo. Ogni commit di spec successivo è «Add test for...»—neppure uno è refactor o 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
Tappare buchi, non rifare. Questo dettaglio torna dopo.
Quattro ragioni dure:
1. Input e output espliciti. Un test è «dato questo stato → aspettati questo comportamento». È il punto forte di Claude: tradurre una spec in asserzione. Il codice di business richiede talvolta compromessi; i test quasi mai.
2. Meccanico × grande volume. Un singolo describe .open! deve coprire «con giurati ammissibili / senza / senza topic / review già attiva»—quattro context, con 2–5 it ciascuno. L'umano inizia a tagliare gli angoli al terzo context. Claude scrive l'88° it con la stessa cura del primo.
3. Ciclo di feedback brevissimo. Scrivi un test, lanci rspec, in secondi sai se passa. Il codice di business richiede giorni di uso reale prima che i problemi emergano. Ciclo breve = qualsiasi errore di Claude viene beccato da rspec sul posto—non serve sorvegliare.
4. Naturalmente parallelo. I blocchi it sono indipendenti, senza accoppiamento nascosto, scalano in modo banale. Generare decine di test isolati in un colpo è esattamente il punto forte di Claude.
Qui sta il perno dell'intera ripartizione.
Ignorare:
Guardare:
L'ultimo punto è il valore vero della review. Claude copre i test «che gli vengono in mente», ma quelli che non gli vengono in mente non si scrivono da soli. È esattamente lì che si incastra la review umana—risalire dalle regole di business alla copertura mancante.
Apri l'inizio del describe ".open!" in spec/services/topic_review_service_spec.rb:
describe ".open!" do
context "when there are eligible jurors" do
# stato review ok / post under_review / assignments create / author notificato / jurors notificati / nessun doppio open
end
context "when there are no eligible jurors" do
# review creata ma non assignments
end
context "when post has no topic" do
# restituisce nil
end
end
Sembra completo. Ma la vera regola di eligible_jurors nel model esclude tre gruppi:
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
Ora guarda i test—quale test afferma che «l'autore del post non è mai selezionato come giurato»?
Cerca in service_spec.rb e model_spec.rb: niente. model_spec.rb testa solo alcuni casi dello scope pending_vote_by; non copre eligible_jurors direttamente. In service_spec.rb c'è solo un commento: # Jurors must NOT be the post author—è nel setup, non un'asserzione.
Ecco cosa la review prende: tre regole di esclusione (autore / segnalante / già-votato-in-initial), e nessuna è protetta da un test. Se in seguito qualcuno refattorizza eligible_jurors e per sbaglio toglie post.user_id dalla lista di esclusione, tutti i test esistenti passano—e la produzione lascia silenziosamente che gli autori siedano nella propria giuria.
Claude non ha sbagliato—ha testato quel che gli è stato chiesto. Semplicemente non ha chiesto da solo: «ciascuna di queste tre regole ha bisogno di copertura di test?» Quella domanda—dalle regole alla copertura a ritroso—è il lavoro della review.
(Onestà: anch'io l'ho mancato nella review iniziale. L'ho notato solo in un secondo audit mentre scrivevo questo post. Dunque anche la review non è one-shot—ma resta comunque 10× meglio che non fare review.)
Se «Claude scrive + umano rivede» fosse perfetto, non ci sarebbero commit di test dopo quello iniziale. Quel che è successo è più interessante—toppare buchi senza riscritture:
00393fc Add test for finalize! with zero votes (expired review)
3f53304 Add test for finalize! with legacy votes missing reasoning
Il primo è un regression test post-bug—e8cb2db Default to keep verdict when review expires with zero votes è il fix, 00393fc è il test abbinato. Stesso pattern per il secondo, a ruota di abaa22e Fix CloseTopicReviewJob failing due to reasoning validation on old votes.
Questi due commit dimostrano due cose insieme:
«Abbastanza buono + rimediabile continuativamente» è un'asticella molto più realistica di «perfetto». Inseguire la review perfetta è proprio ciò che ti impedisce di delegare i test a Claude. Accettare l'«abbastanza buono» è ciò che mette in moto la ripartizione.
Non tutti i test sono adatti a un handoff totale:
In questi casi guida l'umano e Claude assiste.
Trasformare questa ripartizione in un default eseguibile:
Un programmatore ha meno risorse mentali per leggere test che per leggere codice. I test sono ripetitivi, meccanici, logoranti, necessari. Tutto ciò descrive perfettamente il punto forte di Claude—non si annoia, non si stanca, non taglia gli angoli all'it numero 50.
Il tuo lavoro non è «scrivere test»—è «assicurarti che ogni regola di business sia coperta da un test». Uno è implementazione, l'altro è giudizio. Il giudizio resta a te; l'implementazione va a Claude.
119 spec / 1.562 righe consegnate in un commit e rimaste in piedi oltre due settimane senza rework—non è perché scrivo meglio i test, ma perché non ne ho scritto nessuno. Faccio solo una cosa che Claude non fa: decido quali regole di business meritano protezione.