Free

Deixe o Claude escrever os testes, você só revisa: 1.562 linhas na prática

Quase todas as 1.562 linhas de testes do TopicReview foram escritas pelo Claude. Eu só faço a review — já passou de duas semanas em produção, todos os commits posteriores adicionam testes, nenhum os reescreve. Este post trata do porquê os testes são o alvo ideal para delegar, o que olhar e o que ignorar na review (incluindo um edge case real ausente no spec), e a configuração padrão desse reparto.


O post anterior fechou com "119 specs no verde" para TopicReview. A pergunta real que vem a seguir: quem escreveu esses testes?

Resposta: Claude escreveu quase as 1.562 linhas de código de teste. Eu só reviso. Depois de mais de duas semanas em produção, o padrão de manutenção dessas 1.562 linhas tem sido só adicionar novos testes, nunca reescrever os antigos.

Este post trata de por que os testes são o melhor candidato para delegar ao Claude, o que olhar e o que ignorar na review, e até onde esse reparto aguenta na prática.

Primeiro, os números

Os testes do TopicReview estão em 7 arquivos:

spec/services/topic_review_service_spec.rb   760 linhas (88 testes)
spec/requests/topic_reviews_spec.rb          281 linhas (32 testes)
spec/requests/review_appeals_spec.rb         152 linhas (16 testes)
spec/requests/review_votes_spec.rb           127 linhas
spec/policies/topic_review_policy_spec.rb    109 linhas
spec/jobs/close_topic_review_job_spec.rb      71 linhas (7 testes)
spec/models/topic_review_spec.rb              62 linhas
───────────────────────────────────────────
                                          1.562 linhas

Cobre quatro tipos de teste: service (lógica de negócio), request (controller + integração), policy (autorização com Pundit), job (tarefas agendadas).

O commit inicial d162f1e traz Co-Authored-By: Claude Sonnet 4.6 e aterrissa 1.100+ dessas linhas de uma só vez. Todos os commits de spec subsequentes são "Add test for..."—nenhum é refactor ou 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

Tapando lacunas, não refazendo. Este detalhe volta a importar depois.

Por que os testes são o melhor item para delegar

Quatro razões sólidas:

1. Entradas e saídas explícitas. Um teste é "dado este estado → esperar este comportamento". É o ponto forte do Claude: traduzir uma especificação para uma asserção. Código de negócio às vezes exige equilibrar trade-offs; testes, quase nunca.

2. Mecânico × alto volume. Um único describe .open! tem que cobrir "com jurados elegíveis / sem jurados / sem topic / review já ativo"—quatro contexts, 2–5 it cada. O humano começa a cortar no terceiro context. O Claude escreve o it número 88 com o mesmo cuidado do primeiro.

3. Ciclo de feedback curtíssimo. Escreve um teste, roda rspec, em segundos sabe se passa. Código de negócio leva dias de uso real para expor problemas. Ciclo curto = qualquer erro do Claude é pego pelo rspec na hora—você não precisa vigiar.

4. Naturalmente paralelo. Os blocos it são independentes, sem acoplamento oculto, escalam trivialmente. Gerar dezenas de testes isolados de uma vez é exatamente o forte do Claude.

O que olhar na review, o que ignorar

Aqui está o pivô do reparto.

Ignorar:

  • Se a sintaxe RSpec está correta → Claude quase nunca erra
  • Qualidade do mock → a menos que haja over-mock óbvio, está bom
  • Estética das factories → não importa, se roda, roda
  • Consistência de estilo → se algo está fora, o Claude arruma tudo com uma instrução

Olhar:

  • Se os edge cases estão de fato cobertos
  • Se os nomes dos testes descrevem o comportamento esperado real
  • Se há testes que deveriam existir mas estão faltando

O último item é o valor real da review. O Claude cobre os testes "que ele consegue imaginar", mas os que ele não imagina não se escrevem sozinhos. É exatamente aí que a review humana encaixa—caminhar de trás para frente, das regras de negócio para a cobertura faltante.

Um exemplo concreto: o que a review realmente pega

Abrir o início do describe ".open!" de spec/services/topic_review_service_spec.rb:

describe ".open!" do
  context "when there are eligible jurors" do
    # status do review ok / post under_review / assignments criados / author notificado / jurors notificados / não abre duas vezes
  end
  context "when there are no eligible jurors" do
    # review é criado mas assignments não
  end
  context "when post has no topic" do
    # retorna nil
  end
end

Parece abrangente. Mas a regra real de eligible_jurors no model exclui três 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

Agora olhe os testes—qual teste afirma que "o autor do post nunca é selecionado como jurado"?

Vasculha service_spec.rb e model_spec.rb: não existe. model_spec.rb só testa alguns casos do scope pending_vote_by; não cobre eligible_jurors diretamente. service_spec.rb tem só um comentário: # Jurors must NOT be the post author—é parte do setup, não uma asserção.

É isso que a review pega: três regras de exclusão (autor / denunciante / já-votou-na-fase-inicial), e nenhuma delas está protegida por um teste. Se alguém depois refatorar eligible_jurors e sem querer derrubar post.user_id da lista de exclusão, todos os testes existentes passam—e a produção silenciosamente deixa autores sentarem no próprio júri.

O Claude não errou—testou o que pediram. Só não perguntou por conta própria: "cada uma dessas três regras precisa de cobertura de teste?" Essa pergunta—de regras para cobertura, de trás para frente—é o que a review faz.

(Honestidade: eu mesmo deixei isso passar na review original. Só pesquei numa segunda auditoria enquanto escrevia este post. Então a review também não é one-shot—mas continua sendo 10× melhor do que não revisar.)

Os commits subsequentes provam que o reparto funciona na prática

Se "Claude escreve + humano revisa" fosse perfeito, não haveria commits de teste novos depois do inicial. O que aconteceu é mais interessante—tapando lacunas sem reescrituras:

00393fc Add test for finalize! with zero votes (expired review)
3f53304 Add test for finalize! with legacy votes missing reasoning

O primeiro é regression test pós-bug—e8cb2db Default to keep verdict when review expires with zero votes foi o fix, 00393fc foi o teste emparelhado. Mesmo padrão para o segundo, seguindo abaa22e Fix CloseTopicReviewJob failing due to reasoning validation on old votes.

Esses dois commits provam duas coisas ao mesmo tempo:

  • A review não pegou 100% dos casos—por isso produção expôs dois bugs
  • Mas a arquitetura de testes aguentou e dava para seguir adicionando testes sem reestruturar—por isso os commits dizem "Add test for..." e não "Rewrite ... spec"

"Bom o suficiente + dá para seguir remendando" é uma régua muito mais realista do que "perfeito". Perseguir review perfeita é o que te impede de delegar testes ao Claude. Aceitar "bom o suficiente" é o que faz o reparto começar a girar.

Testes que você não deveria delegar por completo ao Claude

Nem todo teste serve para handoff total:

  • E2E happy-paths—exigem lente de produto. O Claude escreve, mas tende a cobrir "tecnicamente completável" e perder "onde o usuário realmente trava"
  • Testes de segurança—exigem mentalidade de atacante. O Claude é conservador e perde superfícies de ataque não-padrão (injeção de palavras-chave SQL, strings gigantes, unicode alternativo)
  • Baselines de performance—exigem números do ambiente real. O Claude chuta limiares
  • Reestruturações grandes de fixture / factory—isso é mudança de nível arquitetural; volta ao plan mode, não é algo que a review pesca

Nesses casos, humano lidera e o Claude auxilia.

Configuração padrão

Para transformar esse reparto em um default executável:

  1. Antes de começar a feature, eu explico as regras de negócio (não as convenções de RSpec)
  2. O Claude escreve implementação e testes
  3. Rodamos os testes. Passou = seguir. Falhou = Claude conserta sozinho
  4. Eu reviso:
    • Nada de sintaxe / mock / factory
    • Cobertura: cada regra de negócio está protegida por pelo menos um teste?
    • Interrogar edge cases: "0 linhas / null / concorrência / violação de autz"—um por um
    • Ler nomes de testes—se o nome não me diz o que é testado, peço ao Claude para renomear
  5. Bugs descobertos em produção voltam como regression tests—é o desgaste normal do reparto, não fracasso

Encerrando

Programadores têm menos energia mental para ler testes do que para ler código. Testes são repetitivos, mecânicos, desgastantes, necessários. Tudo isso descreve o sweet spot do Claude—ele não enjoa, não cansa, não corta caminho no it número 50.

Seu trabalho não é "escrever testes"—é "garantir que toda regra de negócio esteja coberta por um teste". Um é implementação, o outro é julgamento. O julgamento fica com você; a implementação vai para o Claude.

119 specs / 1.562 linhas entregues num commit e sobrevivendo mais de duas semanas sem rework—não aconteceu porque eu escreva melhor testes, mas porque eu não escrevi nenhum. Só faço uma coisa a mais que o Claude: decido quais regras de negócio merecem proteção.