Claude Code em código legado sem testes: por onde começar sem quebrar o que já funciona

fluxo de Claude Code em código legado com plan mode e characterization test antes de refatorar
Resposta rápida

Usar Claude Code em código legado pede uma ordem diferente: entender, provar e só então mudar. Antes do primeiro prompt, deixe a árvore do git limpa, isole o trabalho com claude –worktree nome e revise as regras em /permissions (a avaliação é deny, depois ask, depois allow). Aí explore em plan mode (Shift+Tab), use o subagent Explore pra ler, rode /init pra gerar o CLAUDE.md e fixe o comportamento atual com um characterization test antes de qualquer refatoração. Se algo escapar, /rewind desfaz edições feitas pelas ferramentas de edição de arquivo, mas não desfaz comando bash.

Fala aí, beleza? Todo mundo já sentiu aquele frio na barriga de abrir um projeto de anos atrás, sem teste nenhum, e ter que mudar UMA coisinha lá dentro

O medo não é da mudança em si

O medo é não conseguir provar que o resto continua funcionando depois dela

O Michael Feathers, no livro Working Effectively with Legacy Code, define código legado de um jeito bem seco: é código sem testes. E o raciocínio dele é direto, sem teste você não tem como saber se a sua mudança melhorou ou piorou o comportamento do sistema

Isso vira o jogo na hora de usar Claude Code em código legado. Não é que a IA seja perigosa ali, é que a base não tem rede de proteção nenhuma, então a ordem de trabalho tem que mudar: entender antes de mexer, criar prova antes de refatorar

Bora ver na prática? 🙂

O que preparar antes de soltar o Claude Code no projeto antigo

Essa parte vem antes por um motivo simples: sem esses freios, nada do resto do post segura nada

É igual mexer em fiação velha, tu desliga o disjuntor primeiro

1. Repositório versionado e árvore limpa. Se o git status não estiver limpo, você perde a única régua confiável pra saber o que foi a IA e o que já era seu. Commita ou guarda o que estiver pendente antes de começar

2. Isole o trabalho num worktree. O próprio Claude Code faz isso pra você: passando --worktree (ou o atalho -w) com um nome, ele cria um worktree isolado e já inicia a sessão nele

claude --worktree modulo-cobranca

Por padrão esse worktree é criado em .claude/worktrees/<nome>/ na raiz do repositório, num branch novo chamado worktree-<nome>. Ou seja, o que a sessão fizer não encosta no seu branch de trabalho

3. Leve os arquivos gitignored junto. Projeto antigo quase sempre depende de um .env que nunca foi versionado. Criando um arquivo .worktreeinclude, os arquivos ignorados pelo git (o .env, por exemplo) passam a ser carregados automaticamente em todo worktree novo

4. Revise as permissões ANTES do primeiro prompt. O comando /permissions abre a interface que lista todas as regras ativas e mostra de qual arquivo settings.json cada uma veio. As permissões ficam em arquivos .claude/settings.json, com listas de allow e deny

E tem um detalhe que vale ouro em base legada: as regras são avaliadas na ordem deny, depois ask, depois allow. O primeiro match nessa ordem decide, e a especificidade da regra NÃO muda essa ordem

Se você ainda está dando os primeiros passos e quer um caminho pra aprender o Claude Code do zero, vale fechar essa base antes de encarar um projeto sem testes

Passo a passo: a ordem de trabalho em uma base sem testes

A espinha da sequência é o fluxo recomendado na documentação oficial de boas práticas: Explore, Plan, Code, Commit. A doc justifica separar pesquisa e planejamento da implementação por um motivo bem prático, evitar que o Claude saia resolvendo o problema errado

Em legado isso pesa o dobro, porque o problema errado ali costuma ser caro

1. Explorar sem poder editar: plan mode

O primeiro contato com a base tem que ser somente leitura. Pra isso existe o plan mode, e dá pra entrar nele de três jeitos:

  • apertando Shift+Tab até a barra de status mostrar o plan mode ativo
  • prefixando um prompt único com /plan
  • iniciando a sessão já nele
claude --permission-mode plan

Nesse modo o Claude lê arquivos, roda comandos de exploração e escreve um plano, mas não edita o código-fonte. Comandos fora do conjunto read-only embutido pedem aprovação, inclusive com o auto-allow do sandbox ligado

E se o plano vier ruim? Shift+Tab de novo sai do plan mode sem aprovar o plano

O erro comum deste passo: achar que sair do plan mode significa aceitar o que foi proposto. Não significa. Sair e aprovar são coisas diferentes, e em legado é bem comum descartar o primeiro plano inteiro

2. Fan-out de leitura com o subagent Explore

Base antiga é grande e cheia de arquivo morto. Ler tudo na sessão principal entope o contexto rápido

O Claude Code traz tipos de subagent embutidos, e entre eles está o Explore, otimizado pra busca rápida e somente leitura no código. Cada subagent roda no próprio context window, com system prompt próprio, acesso a ferramentas específico e permissões independentes. Quando termina, o que volta pra sessão principal é só a mensagem final mais metadados

É como mandar um estagiário ler o arquivo de 4 mil linhas e voltar com o resumo, em vez de despejar as 4 mil linhas na sua mesa

O erro comum deste passo: assumir que o subagent conhece as convenções do projeto. Explore e Plan são os únicos subagents que omitem o CLAUDE.md e o git status. Então o contexto que ele precisa (nome do módulo, pasta, o que você suspeita) vai TODO no prompt, escrito na mão

3. Gravar o mapa num CLAUDE.md

Descobriu como o projeto funciona? Grava, senão você vai redescobrir amanhã

/init

O /init analisa a base (arquivos de pacote, documentação existente, arquivos de configuração e estrutura do código) e gera um CLAUDE.md inicial com comandos de build, instruções de teste, diretórios-chave e convenções detectadas

O CLAUDE.md do projeto pode ficar em ./CLAUDE.md ou em ./.claude/CLAUDE.md. E pra aquelas preferências suas que não devem ir pro versionamento, cria um CLAUDE.local.md na raiz e adiciona ele ao .gitignore

Vale saber COMO isso é carregado: os arquivos CLAUDE.md e CLAUDE.local.md na hierarquia de diretórios acima do diretório de trabalho entram por inteiro no início da sessão, enquanto os de subdiretórios carregam sob demanda, quando o Claude lê arquivos daqueles diretórios

O erro comum deste passo: despejar preferência pessoal ("sempre me responda em português", "eu odeio tal padrão") no arquivo versionado, que o time inteiro vai carregar em toda sessão. Isso é vida de CLAUDE.local.md

4. Fixar o comportamento atual com characterization test

Aqui é o passo que quase todo mundo pula, e é o mais importante

Characterization test é um conceito do Feathers: é um teste que caracteriza o comportamento REAL do código hoje. Ele não verifica se o código está correto, ele só fixa o que a coisa faz atualmente, servindo pra preservar e documentar o comportamento existente antes da mudança

Ou seja, ele não julga. Ele fotografa

O prompt muda de cara quando você entende isso:

Escreva testes que capturem o comportamento ATUAL da função
calculaDesconto, incluindo os casos esquisitos e os que parecem
errados. Não corrija nada. Se o retorno de hoje for estranho,
o teste deve esperar esse retorno estranho mesmo assim.

O erro comum deste passo: pedir "escreva testes pra esse módulo" e aceitar o que vier. Você recebe teste do comportamento IDEAL, ele falha na primeira rodada, e aí você vai "consertar" o código antes de ter qualquer prova do que ele fazia. Exatamente a ordem invertida

5. Planejar a mudança e EDITAR o plano

Com o mapa gravado e o comportamento fotografado, aí sim volta pro plan mode e pede o plano da mudança

E não precisa aceitar o plano como veio: Ctrl+G abre o plano no editor de texto pra edição direta, antes do Claude prosseguir

Tome cuidado aqui! Em legado, corrigir uma linha do plano custa segundos, corrigir a mesma linha depois de virar código espalhado em oito arquivos custa a tarde

O erro comum deste passo: aprovar um plano que menciona "limpar", "padronizar" ou "aproveitar e ajustar" coisas que você não pediu. Corta isso do plano com Ctrl+G e deixa pra outra sessão

6. Abrir o /rewind antes de refatorar, mudar em fatia pequena e commitar

Antes de aprovar a primeira fatia de refatoração, abre o botão de emergência e olha ele com calma: rodar /rewind, ou apertar Esc duas vezes com o campo de prompt vazio, abre o menu de rewind

Ele lista cada prompt enviado na sessão, e ao escolher um ponto você tem quatro ações: restaurar código e conversa, restaurar conversa, restaurar código, ou resumir a partir dali

Fazer isso ANTES muda o tamanho da fatia que você se permite arriscar, porque você já sabe até onde consegue voltar (os limites do checkpointing eu abro logo abaixo, e eles são bem específicos)

Aí sim vem a última fase do fluxo oficial, o Commit, que existe pra fechar o ciclo em pedaços pequenos: uma fatia, teste rodando, commit

Depois volta pro Explore da próxima fatia

Quanto menor a fatia, mais barato é o "opa, não era isso"

O erro comum deste passo: deixar cinco mudanças acumularem sem commit intermediário, porque "tá indo bem". Aí quando quebra, você não tem ponto de retorno limpo e o histórico não te ajuda a achar o culpado

O que delegar de cara e o que jamais delegar de primeira

A régua aqui não é confiança, é reversibilidade

Tarefa que a ferramenta consegue desfazer, ou que nem toca no fonte, entra cedo. Tarefa cujo estrago mora FORA do sistema de arquivos fica pra depois, quando já existe teste protegendo

Tarefa Delega de cara? Por quê
Ler e explicar um módulo Sim plan mode não edita o fonte
Mapear dependências e chamadas Sim leitura pura, cabe no subagent Explore
Rascunhar characterization test Sim, revisando teste novo é arquivo novo, fácil de descartar
Resumir o que um script antigo faz Sim não muda nada, só te dá contexto
Migração de esquema de banco Não o efeito não vive no sistema de arquivos
Script destrutivo em arquivo ou dado Não os rm -rf da vida não perdoam
Comando de infraestrutura Não precisa de regra deny e aprovação humana
Mudar comportamento sem teste que o proteja Não é exatamente o que o passo 4 existe pra evitar

Essa separação não é achismo, ela se apoia no que a ferramenta garante de verdade

O plan mode garante que o fonte não é editado. O checkpointing garante o retorno das edições feitas pelas ferramentas de edição de arquivo do Claude, e só delas. E uma regra deny ampla barra família inteira de comando: um Bash(aws *) no deny bloqueia toda chamada que casa, INCLUSIVE as que também casam com um allow mais estreito tipo Bash(aws s3 ls). A mesma precedência vale entre ask e allow

Repara no que sobra de fora dessas três garantias: banco de dados, chamada de API, arquivo mexido na mão. É bem aí que mora o que você não delega de primeira

Quando a base já tiver teste segurando o comportamento, a conversa muda de figura e dá pra partir pra refatorar um projeto legado com muito mais folga

Quando algo dá errado: sintomas comuns e como voltar atrás

Vai dar errado alguma hora, e tudo bem. O que não pode é você descobrir o botão de emergência DEPOIS de precisar dele

Sintoma: "quero desfazer o que ele acabou de fazer"

Causa: uma sequência de edições que pareceu boa nos primeiros dois arquivos e azedou no terceiro

Solução: aquele menu de rewind do passo 6, agora valendo. Escolhe o prompt anterior ao estrago e decide entre restaurar código e conversa, restaurar conversa, restaurar código, ou resumir a partir dali

Agora os limites duros, e eles são importantes: os checkpoints cobrem APENAS as edições feitas pelas ferramentas de edição de arquivo do Claude. Edições que você fez na mão e comandos bash não são rastreados

Tem mais: ao escolher restaurar código (ou código e conversa), o Claude Code pula qualquer caminho rastreado que seja symlink ou hard link, e mostra um aviso. E os checkpoints não são eternos, eles são apagados junto com as sessões depois de 30 dias

Como prevenir: commit pequeno e frequente, como no passo 6. O rewind é rede de segurança da sessão, não substituto de git

Sintoma: "o Claude parou de entender o projeto"

Causa: contexto lotado. Depois de horas lendo arquivo velho e log gigante, o histórico da conversa vira uma pilha de coisa que já não importa

Solução: começa medindo. O comando /context mostra um detalhamento ao vivo por categoria, com sugestões de otimização, incluindo quais arquivos CLAUDE.md e de memória automática foram carregados

Com isso na mão, você escolhe:

  • /compact resume a conversa até ali e substitui o histórico por esse resumo, liberando contexto. Ele aceita opcionalmente instruções de foco pro resumo, tipo pedir pra manter o que foi descoberto sobre um módulo específico
  • /clear inicia uma conversa nova com contexto vazio. A anterior continua acessível pelo /resume, e o CLAUDE.md e os arquivos do projeto seguem disponíveis, só o histórico de chat sai

Como prevenir: o passo 3 de novo. Descoberta que está gravada no CLAUDE.md sobrevive ao /clear, descoberta que só existe no chat morre com ele

Sintoma: "ele rodou um comando que eu não queria"

Causa: regra de permissão frouxa, ou allow que você achou que estivesse coberto por algo mais específico

Solução: abre o /permissions e olha de qual settings.json a regra veio. Lembrando da ordem: deny, depois ask, depois allow, primeiro match decide

Pra caso que exige lógica, e não só padrão de texto, dá pra usar hook. Em hooks PreToolUse, o exit code 2 bloqueia a chamada da ferramenta e o stderr vira a mensagem de erro. O PreToolUse também permite allow, deny, ask ou defer da chamada

E aqui vai a pegadinha que morde geral: sem JSON válido no stdout, o Claude Code trata o exit code 1 como erro NÃO bloqueante e segue com a ação, mesmo o 1 sendo o código de falha convencional no Unix. Se o seu hook de proteção sai com 1 achando que travou tudo, ele não travou nada… o número que bloqueia é o 2. E se o hook sai com 2, qualquer JSON é ignorado

Conclusão

A ordem é o conteúdo inteiro deste post: entender, provar, então mudar

Em base legada, a pressa de pular direto pro "muda isso aqui" é exatamente o que transforma uma tarefa de trinta minutos num dia inteiro caçando o que quebrou

Se quiser um próximo passo pequeno e concreto pra hoje: roda /init no projeto antigo, abre uma sessão em plan mode e sai dela com um characterization test do módulo que te dá mais medo

Sem encostar no código de produção

Só isso já te dá algo que o projeto não tinha ontem: prova

até o próximo post! 😀

Perguntas frequentes

Como desfazer uma alteração do Claude Code em código legado sem perder o histórico da conversa?

Rodando /rewind, ou apertando Esc duas vezes com o campo de prompt vazio, abre o menu de rewind. Ele lista cada prompt enviado na sessão e, ao escolher um ponto, dá pra restaurar código e conversa, só a conversa, só o código, ou resumir a sessão a partir dali.

O checkpointing do Claude Code cobre qualquer mudança feita no código legado?

Não. Os checkpoints cobrem só as edições feitas pelas ferramentas de edição de arquivo do próprio Claude, edição manual sua e comando bash não entram nesse rastro. Symlinks e hard links também ficam de fora: ao restaurar código, o Claude Code pula esses caminhos e avisa.

Por quanto tempo os checkpoints do Claude Code ficam disponíveis num projeto antigo?

30 dias. Depois desse prazo o Claude Code apaga os checkpoints junto com as sessões, então não dá pra contar com eles como backup de longo prazo.

Qual a diferença entre /clear e /compact ao mexer numa base sem testes?

/clear inicia uma conversa nova com contexto vazio, mas a conversa anterior continua acessível pelo /resume e o CLAUDE.md com os arquivos do projeto seguem disponíveis. Já o /compact resume o que já foi conversado e substitui o histórico por esse resumo, liberando espaço no contexto sem cortar o fio da meada.

O que é um characterization test e por que ele importa antes de refatorar código legado?

É um conceito do Michael Feathers: um teste que caracteriza o comportamento real do código hoje, sem julgar se esse comportamento está certo. Ele serve pra fixar e documentar o que o sistema faz atualmente, criando uma referência antes de qualquer mudança.

Dá para bloquear automaticamente uma ação arriscada do Claude Code num projeto sensível?

Sim, com um hook PreToolUse: se ele sair com exit code 2, a chamada da ferramenta é bloqueada e o stderr vira a mensagem de erro mostrada. Vale reparar que exit code 1 sem JSON válido no stdout NÃO bloqueia nada, o Claude Code trata como erro não bloqueante e segue com a ação.



Escrito por | Matheus Battisti

Matheus Battisti
Fundador da Hora de Codar

Programador apaixonado pelo mundo das tecnologias, sempre buscando em aprender e se aprofundar em linguagens, frameworks e o que mais for necessário para executar um bom trabalho. Agora tem uma nova missão que é de passar seu conhecimento adiante para formar novos programadores e especializar mais os que já são.

Formações

Formação Vibe Coding

Formação Vibe Coding

Do Prompt ao Produto: Crie Software Real com IA

  • 474 aulas
  • 20 projetos
  • 39h 30min

Blog | Mais populares