Free

3.000 linhas em um único commit: por que features complexas exigem plan mode primeiro

O instinto diante de features complexas é "deixa eu testar algo" — mas as decisões arquiteturais se escondem no terceiro modelo, no quinto edge case. O verdadeiro valor do plan mode é mover a conversa em nível de arquitetura para o texto antes do código. Caso real: o sistema de júri comunitário do Pickful — 3.032 linhas, 119 specs no verde, um único commit, zero retrabalho arquitetural depois.


Diante de uma feature complexa, o instinto da maioria é "deixa eu testar alguma coisa".

O problema: sistemas complexos têm uma característica — as decisões arquiteturais se escondem no 3º modelo, no 5º edge case, na 8ª regra de pontos. Mergulhar de cabeça no código e esbarrar nessas decisões no meio do caminho, voltando para mudá-las, custa 10× mais do que ter discutido tudo em texto desde o início.

Usei o plan mode do Claude Code para construir o TopicReview, o sistema de júri comunitário do Pickful, e vi a versão extrema dessa desigualdade: um único commit, 3.032 linhas, 119 specs no verde, entregue de uma vez só. Nenhum dos dez e tantos commits que vieram depois foi um retrabalho arquitetural — todos são ajustes de parâmetros, polimento de UI, correção de edge cases. O sistema está rodando estável desde então e hoje é central para como a comunidade se automodera.

Este post é sobre por que features complexas exigem plan mode primeiro, o que a fase de plan realmente está fazendo, e quando a conversa está madura o suficiente para começar a escrever código.

Quão complexo é o sistema

TopicReview é uma votação comunitária para decidir se um post de baixa qualidade é removido. Dá para resumir em uma frase — mas a especificação se desdobra em camadas:

  • 5 estados: open → voting → decided → appealed → closed
  • 3 veredictos: remove / warn / keep
  • 2 fases: revisão inicial por 12 jurados; apelação por 5 juízes (sorteados entre os 20 usuários com mais pontos)
  • Fluxo multidimensional de pontos: penalidade provisória de 10 pt, stake de apelação de 10 pt, +5 para jurados que votaram com o veredicto, +10 para juízes que votaram com o veredicto, +3 para denunciantes cujo report foi validado, reembolso ao ganhar uma apelação = stake + bonus + penalidade provisória
  • 4 tipos de jobs agendados: janela de votação inicial de 24h, janela de apelação de 24h, janela de revisão da apelação de 24h, e — pontos dos jurados diferidos 24h após um veredicto remove (porque uma apelação pode revertê-lo)
  • Fluxos paralelos + rollback: se os votos se invertem durante a remoção provisória, o post precisa ser restaurado e a penalidade provisória devolvida; se a apelação reverte o veredicto, o stake é devolvido com bônus, possivelmente junto com a penalidade provisória, e os jurados são recalculados pelo novo veredicto

A complexidade não está em nenhuma regra isolada — está em como as regras interagem. Cada regra nova pode disparar um rollback em outra.

Paredes contra as quais você esbarra sem plan

Quando você escreve direto, os problemas mais difíceis não são os fatos visíveis — são as perguntas que você nunca pensou em fazer. Lendo o código do TopicReview de trás para frente, há pelo menos quatro paredes que inevitavelmente baterão em você se pular o plan:

Regras de elegibilidade de jurados. Parece ser apenas User.jurors_and_judges.sample(12). Mas as regras reais são: excluir o autor do post, excluir quem denunciou o post, excluir quem já votou na rodada inicial (para não votar também na apelação). Três exclusões empilhadas. Escrevendo o model de uma vez só, você vai esquecer uma ou duas.

Pagamento diferido de pontos. Um veredicto remove normalmente dispara o pagamento aos jurados. Mas uma apelação pode reverter um remove — e quando reverte, os jurados que votaram com o veredicto antigo ficam do lado errado, então os pagamentos precisam ser recalculados contra o novo veredicto. Por isso o veredicto remove deve esperar a janela de apelação de 24h fechar antes de pagar os jurados. Pule essa regra, pague primeiro, e você acaba tentando reaver pontos — 10× mais complicado do que pagar tarde.

Design da interface do job agendado. CloseTopicReviewJob parece "finalizar uma review". Na prática ele lida com três situações:

# Expiração da janela de votação inicial
CloseTopicReviewJob.set(wait_until: voting_ends_at).perform_later(review.id)
# Pagamento diferido aos jurados após um veredicto remove
CloseTopicReviewJob.set(wait: 24.hours).perform_later(review.id, award_juror_points: true)
# Expiração da janela de apelação
CloseTopicReviewJob.set(wait: 24.hours).perform_later(review.id, appeal_id: appeal.id)

Sem planejamento antes, você escreve a primeira assinatura e no segundo caso descobre que a interface precisa mudar.

A parede mais cara: dá para apelar durante a remoção provisória? Quando os votos remove cruzam o limiar, o post é escondido (provisório), mas a review continua no estado voting — não decided. O usuário já pode apelar?

  • Pode → precisa expandir as transições entre decided e appealed
  • Não pode → UX ruim: o post está escondido e o usuário tem que esperar 24h para poder protestar
  • Escolha real do TopicReview: sim, apelar durante a remoção provisória é permitido — mas a review tem que passar primeiro por finalize → decided antes de abrir a apelação

Essa única decisão volta e muda os checks de parâmetros dentro de open_appeal! e a lógica da máquina de estados. Decidir no meio do caminho = reescrever metade do sistema.

O que a fase de plan está realmente fazendo

O plan mode do Claude Code é um modo onde escrever código é proibido — Claude pode ler o repo, pensar em abordagens, discutir com você, mas qualquer modificação de arquivo é bloqueada via hard-block até você aprovar um plano.

Essa restrição mecânica é o ponto: ela força a conversa em nível de arquitetura a acontecer em texto.

Algumas coisas que a fase de plan está fazendo na prática:

1. Desenhar a máquina de estados e os papéis. 5 estados, 4 papéis (jurado / juiz / autor / denunciante), o que cada papel pode e não pode fazer em cada estado — tudo em algumas linhas de markdown. Algumas linhas vs. dezenas de arquivos. O custo de mudança está a duas ordens de grandeza de distância.

2. Percorrer o fluxo de cada papel:

  • Jurado: recebe notificação → abre a review → lê o post e o motivo → vota com reasoning → recebe pontos
  • Autor do post: recebe notificação → vê o veredicto → se for remove, considera apelar → deposita o stake → espera
  • Juiz: recebe a apelação sorteada do top-20 → vota → recebe pontos

Onde o percurso trava, uma pergunta oculta salta: "os jurados veem os votos uns dos outros?", "o que o autor pode fazer durante a remoção provisória?"

3. Interrogar edge cases. A fase de plan não desenha o "caminho normal" — ela faz de propósito as perguntas que normalmente não aparecem:

  • E se a janela fechar com 0 votos? (Escolha final: keep por padrão.)
  • E se a apelação fechar com 0 votos? (dismissed; stake perdido.)
  • Um juiz pode votar num post que ele mesmo denunciou? (Não — mesma regra de exclusão.)
  • Vários jurados disparam finalize ao mesmo tempo — a race dobra o pagamento? (Adicionar um campo juror_points_awarded para idempotência.)

95% dessas perguntas nunca aparecem naturalmente enquanto você escreve código. A fase de plan força você a responder uma a uma.

4. O livro-razão de pontos. O sistema de pontos é complexo o suficiente para que discutir não baste — é preciso desenhar a tabela de verdade: cada fluxo de pontos com seu disparador, valor e caminho de rollback. Quando o livro fecha, todos os edge cases (reembolso provisório, reembolso de apelação, bônus) se reconciliam.

Até onde é preciso conversar antes de começar a codar

Um critério duro:

  • Você consegue percorrer cada caminho sem travar — cada combinação desde abrir a review até closed (keep / remove / warn × com / sem apelação × provisório / não) — você consegue contar de ponta a ponta
  • Cada edge case tem um comportamento definido — não "depois a gente vê", mas "0 votos por padrão é keep", "provisório permite apelar mas precisa finalize primeiro"
  • Não tem "ah, isso eu ainda não pensei" — toda pergunta que você consiga formular já tem resposta

Quando atinge esse patamar, escrever código é traduzir o plano para Ruby.

Como se parece a fase de execução de uma vez

A primeira entrega desse sistema:

d162f1e Add community moderation system with jury/judge review and appeals
  63 files changed, 3032 insertions(+)
  119 specs, 0 failures

63 arquivos, 3.032 linhas, 119 specs no verde. Entregue de uma vez só.

Os dez e tantos commits seguintes confirmam que a arquitetura segurou:

Apply legacy penalty (1pt) for posts created before 2026-03-26
Fix topic review appeal bugs: window mismatch and verdict not updated
Add topic_removed status for posts removed by community review
Default to keep verdict when review expires with zero votes
Reduce provisional removal penalty to 1pt during trial period
Allow topic creator to withdraw active reviews
Add handled tab to jury dashboard, fix tabs styling

Cada um é ajuste / fix / polish / pequena feature. Nenhum é "voltar e redesenhar a máquina de estados" ou "o modelo de pontos precisa mudar". O esqueleto estava certo.

Essa é a verdadeira recompensa do plan: a execução não é interrompida. Não tem "espera, como esse caminho funciona" — o plano já cobriu. Não tem "não pensei nesse edge case" — a fase de plan perguntou. Não tem "essa interface precisa ser redesenhada" — o plano já resolveu.

Horas escrevendo código, fazendo uma única coisa: traduzir um design claro para o teclado.

Quando não usar plan

Nem toda tarefa merece plan:

  • Fixes de bugs simples — localizar + consertar + adicionar regression test; sem discussão prévia
  • Refactors mecânicos — renomear, extrair, mover; caminho único, plan é um passo a mais
  • Um único caminho óbvio — adicionar um endpoint GET, um toggle de UI; não tem o que debater
  • Protótipos exploratórios — você mesmo não sabe o que quer; fazer algo rústico que rode vence pensar mais

O benefício do plan mode vem de baixar o custo de reescrita. Se a tarefa não tem risco de reescrita, plan é só overhead.

Fechando

"Pense antes de agir" soa como conselho de personalidade. Plan mode não é isso.

Plan mode é fazer a conversa em nível arquitetural acontecer onde 10 palavras podem mudá-la — não onde 30 arquivos precisam mudar.

Sob complexidade, aquele velho ditado da indústria "words are cheap, code is expensive" precisa ser invertido — não significa "escreve código primeiro e vê" (isso só vale quando a diferença de custo é pequena), significa usar essa diferença: colocar a conversa cara na frente, no meio barato.

3.000 linhas em um único commit é uma sensação incrível. Não porque eu escrevo rápido — porque o plano transformou "escrever" em um ato puramente mecânico.