Casi las 1.562 líneas de tests de TopicReview las escribió Claude. Yo solo reviso — ya más de dos semanas en producción, todos los commits posteriores añaden tests, ninguno los reescribe. Este post trata de por qué los tests son el blanco ideal para delegar, qué mirar y qué ignorar en la review (incluido un edge case real faltante en el spec), y la configuración por defecto para este reparto.
El post anterior cerraba con "119 specs en verde" para TopicReview. La pregunta real que sigue: ¿quién escribió esos tests?
Respuesta: Claude escribió casi las 1.562 líneas de código de test. Yo solo reviso. Llevamos más de dos semanas en producción, y el patrón de mantenimiento de esas 1.562 líneas ha sido solo añadir tests nuevos, nunca reescribir los viejos.
Este post trata de por qué los tests son el mejor candidato para delegar en Claude, qué mirar y qué ignorar al revisar, y hasta dónde aguanta este reparto en la práctica.
Los tests de TopicReview se reparten en 7 archivos:
spec/services/topic_review_service_spec.rb 760 líneas (88 tests)
spec/requests/topic_reviews_spec.rb 281 líneas (32 tests)
spec/requests/review_appeals_spec.rb 152 líneas (16 tests)
spec/requests/review_votes_spec.rb 127 líneas
spec/policies/topic_review_policy_spec.rb 109 líneas
spec/jobs/close_topic_review_job_spec.rb 71 líneas (7 tests)
spec/models/topic_review_spec.rb 62 líneas
───────────────────────────────────────────
1.562 líneas
Cubre cuatro tipos de test: service (lógica de negocio), request (controller + integración), policy (autorización con Pundit), job (tareas programadas).
El commit inicial d162f1e lleva Co-Authored-By: Claude Sonnet 4.6 y aterriza 1.100+ de esas líneas de una sola vez. Todos los commits de spec posteriores son "Add test for..."—ni uno es 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
Parches de huecos, no rehacer. Este detalle vuelve después.
Cuatro razones duras:
1. Entradas y salidas explícitas. Un test es "dado este estado → espera este comportamiento". Ese es el punto fuerte de Claude: traducir especificación a aserción. El código de negocio a veces exige sopesar compromisos; los tests, casi nunca.
2. Mecánico × alto volumen. Un solo describe .open! tiene que cubrir "con jurados elegibles / sin jurados / sin topic / review ya activo": cuatro context, 2–5 it cada uno. El humano empieza a recortar esquinas en el tercer context. Claude escribe el it número 88 con la misma diligencia que el primero.
3. Ciclo de feedback brevísimo. Escribes un test, corres rspec, sabes en segundos si pasa. El código de negocio tarda días de uso real en exponer problemas. Ciclo corto = cualquier error de Claude lo atrapa rspec al momento—no necesitas vigilar.
4. Naturalmente paralelo. Los bloques it son independientes, sin acoplamiento oculto, trivialmente escalables. Generar decenas de tests aislados a la vez es exactamente para lo que Claude es bueno.
Aquí está el pivote del reparto.
Ignorar:
Mirar:
Lo último es el verdadero valor de la review. Claude cubre los tests "que se le ocurren", pero los que no se le ocurren no se escriben solos. Ahí encaja exactamente la review humana: retroceder de las reglas de negocio a la cobertura faltante.
Abrimos el inicio del describe ".open!" de spec/services/topic_review_service_spec.rb:
describe ".open!" do
context "when there are eligible jurors" do
# estado del review ok / post under_review / assignments creados / author notificado / jurors notificados / no se abre dos veces
end
context "when there are no eligible jurors" do
# se crea el review pero no los assignments
end
context "when post has no topic" do
# devuelve nil
end
end
Parece completo. Pero la regla real de eligible_jurors en el model excluye a tres grupos:
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
Ahora miramos los tests—¿qué test afirma que "el autor del post nunca es seleccionado como jurado"?
Buscas en service_spec.rb y model_spec.rb: no hay. model_spec.rb solo testea unos casos del scope pending_vote_by; no cubre eligible_jurors directamente. En service_spec.rb solo hay un comentario: # Jurors must NOT be the post author—es parte del setup, no una aserción.
Esto es lo que atrapa la review: tres reglas de exclusión (autor / denunciante / ya-votó-en-inicial), y ninguna está protegida por un test. Si alguien refactoriza eligible_jurors y sin querer quita post.user_id de la lista de exclusión, todos los tests existentes pasan—y la producción deja calladamente que los autores se sienten en su propio jurado.
Claude no se equivocó—testeó lo que le pidieron testear. Solo que no preguntó por su cuenta: "¿cada una de estas tres reglas necesita cobertura de test?". Esa pregunta—retroceder de reglas a cobertura—es lo que hace la review.
(Con honestidad: yo mismo me lo pasé por alto en la review original. Solo lo detecté en una segunda auditoría mientras escribía este post. Así que la review tampoco es one-shot—pero sigue siendo 10× mejor que no revisar.)
Si "Claude escribe + humano revisa" fuese perfecto, no habría commits de test nuevos tras el inicial. Lo que pasó es más interesante—parches sin reescrituras:
00393fc Add test for finalize! with zero votes (expired review)
3f53304 Add test for finalize! with legacy votes missing reasoning
El primero es regression test post-bug—e8cb2db Default to keep verdict when review expires with zero votes fue el fix, 00393fc el test emparejado. Mismo patrón para el segundo, siguiendo a abaa22e Fix CloseTopicReviewJob failing due to reasoning validation on old votes.
Estos dos commits prueban dos cosas a la vez:
"Suficientemente bueno + se puede seguir parcheando" es una vara mucho más realista que "perfecto". Buscar review perfecta es lo que impide delegar tests a Claude. Aceptar "suficientemente bueno" es lo que pone en marcha el reparto.
No todos los tests están bien para handoff total:
En esos casos, el humano lidera y Claude asiste.
Para convertir este reparto en un default ejecutable:
Los programadores tienen menos energía mental para leer tests que para leer código. Los tests son repetitivos, mecánicos, agotadores, necesarios. Todo eso describe exactamente el sweet spot de Claude—no se aburre, no se cansa, no recorta en el it número 50.
Tu trabajo no es "escribir tests"—es "asegurarte de que toda regla de negocio esté cubierta por un test". Lo uno es implementación, lo otro es juicio. El juicio se queda contigo; la implementación se va a Claude.
119 specs / 1.562 líneas entregadas en un commit y sobreviviendo más de dos semanas sin rework—no pasó porque yo escriba mejor los tests, sino porque no escribí ninguno. Yo solo hago una cosa que Claude no: decido qué reglas de negocio merecen protección.