Como automatizar refatorações seguras com Claude Code
Workflow prático para refatorar com Claude Code: antes/depois, testes, git diff, revisão, riscos e prompts prontos para usar.
Comece definindo o limite de segurança
A forma mais segura de automatizar refactoring com Claude Code não é pedir “limpe este projeto inteiro”. Isso parece produtivo, mas normalmente gera um diff grande demais para revisar. Refatorar significa melhorar a estrutura interna sem mudar o comportamento que usuários, APIs, testes e sistemas dependentes esperam. Se esse limite fica vago, a automação deixa de ser refactoring e vira mudança funcional acidental.
Neste guia, Claude Code é tratado como um parceiro prático: investiga, propõe um plano pequeno, muda uma única parte, executa comandos de verificação e explica o git diff. A documentação oficial de common workflows ajuda a entender esse estilo. Para permissões de comandos e configuração do projeto, veja também a documentação de settings.
Se sua equipe ainda está criando regras de uso, leia também os guias sobre permissões do Claude Code e gestão de contexto no Claude Code. Aqui vamos para a rotina: o que pedir primeiro, quais mudanças são seguras para iniciantes, como testar e como revisar.
Nota prática do Masa: em testes pequenos, Claude Code foi muito bem em renomear variáveis, extrair funções puras, esclarecer tipos TypeScript e adicionar testes de regressão. Ficou mais difícil quando o prompt era amplo, como “modernize este serviço”. O workflow sem glamour é o mais útil: escopo estreito, testes visíveis e diff pequeno.
Workflow seguro: investigar, planejar, mudar uma coisa e verificar
Use esta ordem até o time confiar no processo.
| Etapa | O que Claude Code faz | O que a pessoa confere |
|---|---|---|
| 1. Investigar | Lê arquivos, dependências e testes | Escopo não ficou amplo demais |
| 2. Planejar | Propõe plano com até três passos | Não há mudança funcional escondida |
| 3. Editar | Muda apenas um tema | O diff cabe numa revisão |
| 4. Verificar | Roda test, typecheck e lint | Falhas estão explicadas |
| 5. Revisar | Resume git diff e riscos | O comportamento before/after é equivalente |
Comece com um prompt sem edição.
Inspecione este repositório e encontre candidatos seguros para refactoring.
Ainda não edite arquivos.
Condições:
- Não mudar comportamento externo
- Manter cada diff em três arquivos ou menos
- Priorizar áreas com testes existentes
- Retornar uma tabela com candidato, motivo, comando de verificação e risco
“Ainda não edite arquivos” é importante. Claude Code pode passar rapidamente da leitura para a ação. Separar investigação e implementação reduz bastante o risco.
Crie uma branch e registre a linha de base.
git status --short
git checkout -b refactor/safe-extract-order-total
npm test
npm run typecheck
npm run lint
Adapte os comandos ao seu package.json. Se os testes já falham antes do refactor, registre isso. Caso contrário, depois você não saberá se Claude Code quebrou algo ou apenas expôs um problema existente.
Use case 1: Renomear e extrair uma função pura pequena
O primeiro exercício seguro é melhorar nomes e extrair uma função pura. Uma função pura retorna a mesma saída para a mesma entrada e não atualiza banco, envia email, chama API ou altera estado global. Esse é um bom terreno para Claude Code porque o sucesso é fácil de testar.
// before: src/domain/order.ts
export function calc(o: { items: { p: number; q: number }[]; d?: number }) {
let t = 0;
for (const i of o.items) {
t += i.p * i.q;
}
if (o.d) {
t = t - o.d;
}
return Math.max(t, 0);
}
O código é curto, mas p, q e d escondem o significado de negócio. Peça a Claude Code para fixar o comportamento com testes antes de melhorar os nomes.
Refatore com segurança a função calc em src/domain/order.ts.
Requisitos:
- Adicionar testes unitários que fixem o comportamento atual antes de mudar a implementação
- Manter o nome exportado calc neste diff
- Melhorar nomes de variáveis e tipos
- Preservar a regra de que o total nunca fica negativo
- Executar npm test -- order depois da mudança
Um bom resultado é pequeno.
// after: src/domain/order.ts
type OrderLine = {
price: number;
quantity: number;
};
type OrderInput = {
items: OrderLine[];
discount?: number;
};
export function calc(order: OrderInput): number {
const subtotal = order.items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
return Math.max(subtotal - (order.discount ?? 0), 0);
}
Testes copiáveis:
// src/domain/order.test.ts
import { describe, expect, it } from "vitest";
import { calc } from "./order";
describe("calc", () => {
it("multiplies price and quantity", () => {
expect(calc({ items: [{ price: 1200, quantity: 2 }] })).toBe(2400);
});
it("applies discount without returning a negative total", () => {
expect(calc({ items: [{ price: 500, quantity: 1 }], discount: 800 })).toBe(0);
});
});
Revise só os arquivos tocados.
git diff -- src/domain/order.ts src/domain/order.test.ts
npm test -- order
npm run typecheck
A pergunta de revisão não é “o código parece elegante?”. É “as mesmas entradas continuam tendo o mesmo significado de negócio?”. Confira cálculo, nome exportado e descrições dos testes.
Use case 2: Remover any começando pela fronteira
Reduzir any é valioso, mas fazer isso no projeto inteiro de uma vez é um erro comum. Comece por fronteiras: respostas de API, formulários, configuração, webhooks e linhas importadas. É ali que dados desconhecidos entram no sistema.
// before: src/lib/user-api.ts
export async function fetchUser(id: string): Promise<any> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
export function getDisplayName(user: any): string {
return user.profile.displayName || user.name;
}
Dê um alvo estreito e inclua o comportamento para dados incompletos.
Reduza o uso de any em src/lib/user-api.ts.
Requisitos:
- Adicionar um tipo para a resposta da API
- Manter a URL do fetch e o significado do retorno
- Tornar getDisplayName seguro quando profile estiver ausente
- Adicionar testes para o comportamento atual do nome exibido
- Executar npm test -- user-api e npm run typecheck
Um primeiro diff aceitável:
// after: src/lib/user-api.ts
export type UserResponse = {
id: string;
name: string;
profile?: {
displayName?: string;
};
};
export async function fetchUser(id: string): Promise<UserResponse> {
const response = await fetch(`/api/users/${id}`);
return response.json() as Promise<UserResponse>;
}
export function getDisplayName(user: UserResponse): string {
return user.profile?.displayName ?? user.name;
}
Esse cast não valida dados em runtime. Se você precisa de segurança real de entrada, faça um segundo diff com zod ou com um parser já usado no projeto. Não misture “remover any” e “adicionar biblioteca de validação” no primeiro diff de iniciante.
// src/lib/user-api.test.ts
import { describe, expect, it } from "vitest";
import { getDisplayName, type UserResponse } from "./user-api";
describe("getDisplayName", () => {
it("uses profile displayName when present", () => {
const user: UserResponse = {
id: "u1",
name: "Masa",
profile: { displayName: "Masa I." },
};
expect(getDisplayName(user)).toBe("Masa I.");
});
it("falls back to name when profile is missing", () => {
expect(getDisplayName({ id: "u2", name: "Guest" })).toBe("Guest");
});
});
Na revisão, procure atalhos perigosos: as any, erros engolidos, string vazia como fallback ou mudanças em campos opcionais. Um diff com mais tipos ainda pode quebrar comportamento.
Use case 3: Dividir uma função grande só depois de criar testes
Funções grandes são tentadoras, mas escondem comportamento. Pedidos, cobrança, permissões, notificações e importações misturam validação, cálculo, persistência e efeitos colaterais. Peça a Claude Code para extrair apenas uma parte pura.
// before: src/services/order-service.ts
export async function createOrder(input: CreateOrderInput) {
if (input.items.length === 0) {
throw new Error("items required");
}
const subtotal = input.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
const shippingFee = subtotal >= 10000 ? 0 : 800;
const total = subtotal + shippingFee;
const order = await db.order.create({
data: { userId: input.userId, subtotal, shippingFee, total },
});
await mailer.sendOrderCreated(order.id);
return order;
}
O prompt deve dizer também o que está fora do escopo.
Deixe createOrder em src/services/order-service.ts menor.
Neste diff:
- Extraia apenas o cálculo de frete e total para uma função pura
- Nomeie como calculateOrderTotals
- Adicione testes unitários para calculateOrderTotals
- Mantenha igual a ordem de escrita no banco e envio de email
Não faça neste diff:
- Alterar schema do banco
- Alterar mensagens de erro
- Alterar formato da resposta da API
- Mover funções não relacionadas
- Formatar o arquivo inteiro
Depois:
// after: src/services/order-service.ts
export function calculateOrderTotals(items: OrderItem[]) {
const subtotal = items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
const shippingFee = subtotal >= 10000 ? 0 : 800;
return {
subtotal,
shippingFee,
total: subtotal + shippingFee,
};
}
Comandos de revisão:
git diff --stat
git diff -- src/services/order-service.ts
git diff -- src/services/order-service.test.ts
npm test -- order-service
Se Claude Code também formatar trechos não relacionados:
Este diff ficou grande demais.
Reverta mudanças apenas de formatação e mantenha somente a extração de calculateOrderTotals e os testes.
Não altere comportamento externo, mensagens de erro, escritas no banco nem ordem de email.
Revise com git diff, não por sensação
A explicação de Claude Code ajuda, mas o diff é a fonte da verdade.
git diff --check
git diff --stat
git diff --name-only
git diff --word-diff -- src/domain/order.ts
| Área | O que conferir |
|---|---|
| Comportamento | Entradas, saídas, exceções, HTTP status e ordem de persistência |
| Tamanho | A mudança cabe numa revisão humana |
| Testes | O comportamento existente está protegido |
| Tipos | Não há novo as any, cast inseguro ou erro ignorado |
| Efeitos | API, email, cobrança, exclusão e permissões mantêm a ordem |
| Resumo | O resumo do Claude Code combina com o diff real |
Prompt de revisão:
Revise este git diff.
Verifique:
- Se a mudança passou do escopo de refactoring
- Qual comportamento não está protegido por testes
- Se há casts inseguros ou erros silenciados
- Quais arquivos uma pessoa deve olhar com cuidado
Retorne:
- Parece seguro
- Precisa de confirmação humana
- Deve corrigir
com nomes de arquivos e motivos.
Pitfall: falhas e riscos comuns
O primeiro failure é um prompt amplo demais.
Deixe esta camada de serviço mais limpa.
Isso pode misturar extração, nomes, desenho de erros, movimento de arquivos e formatação. Prefira:
Extraia apenas o cálculo de frete de createOrder para uma função pura.
Não mude ordem de processamento, mensagens de erro nem valores de retorno.
O segundo risk é aceitar um diff bonito sem testes. A legibilidade pode melhorar enquanto limites mudam: descontos, frete grátis, permissões, retry ou null. O terceiro erro é misturar formatação com refactoring estrutural. Se Prettier altera centenas de linhas, a mudança real some. O quarto risk é abrir permissões de comandos cedo demais. Comece com leitura, test, typecheck e lint; amplie depois.
Checklist reutilizável e CTA
## Refactoring checklist
- [ ] A mudança tem um único objetivo
- [ ] Testes de base rodaram antes da edição
- [ ] O comportamento before/after é equivalente
- [ ] Testes existentes ou novos protegem o comportamento
- [ ] git diff --stat é pequeno o bastante para revisar
- [ ] git diff --check passa
- [ ] Nenhum any, cast inseguro ou erro engolido foi adicionado
- [ ] DB, email, cobrança, exclusão e permissões mantêm a ordem
Prompt final:
Execute um único diff seguro de refactoring.
Alvo:
- src/services/order-service.ts
- src/services/order-service.test.ts
Critérios de sucesso:
- Comportamento externo não muda
- calculateOrderTotals é extraído
- Testes existentes e novos passam
- Reporte git diff --stat e comandos executados
Proibido:
- Alterações de schema DB
- Alterações de resposta API
- Alterações de mensagens de erro
- Edição de arquivos não relacionados
Na minha verificação, duas práticas mais melhoraram a qualidade: pedir um plano sem edição antes de tocar nos arquivos e terminar sempre com resumo de git diff. Combine este workflow com a checklist de revisão do Claude Code e as boas práticas de CLAUDE.md para repetir em equipe.
Se sua equipe quer um modelo seguro para Claude Code, o treinamento Claude Code pode cobrir permissões, prompts, revisão e desenho de workflow. Automação de refactoring gera valor quando reduz risco de manutenção, não quando cria diffs impossíveis de revisar.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.
Artigos relacionados
Workflow Obsidian para CLAUDE.md com Claude Code
Transforme notas de trabalho do Obsidian em notas operacionais CLAUDE.md para preservar contexto.
Claude Code Revenue CTA Routing: artigos para PDF, Gumroad e consultoria
Um fluxo com Claude Code para levar leitores ao PDF grátis, Gumroad ou consultoria conforme intenção.
Regras de handoff para equipes com Claude Code: evidências, permissões, rollback e receita
Formato prático para entregar trabalho do Claude Code com prova, permissões, rollback, PDF grátis, Gumroad e consultoria.