Passada de refatoração
Antigamente, refatorar começava com um pressentimento. O código era difícil de trabalhar: uma classe que você percorria para encontrar onde colocar a novidade, uma função que você mantinha na cabeça para alterar um branch, uma funcionalidade que exigia visitar oito arquivos. O mesmo pressentimento dizia o que deixar em paz. Código feio que você nunca tocava nunca doía, então você o deixava quieto.
Um agente escrevendo código não sente nada disso, e um desenvolvedor que lê principalmente o que o agente escreveu também não. Então os cheiros se acumulam sem serem notados. Um agente adicionará um trigésimo branch a uma função sem reclamar. E quando é solicitado a refatorar, ele arruma o que parece desarrumado, incluindo código que ninguém toca. Esta skill devolve ambas as metades do pressentimento, como três coisas que você pode medir:
- Onde você sentiria: o mapa de calor. O atrito só importa onde as mudanças continuam pousando. Em diversos estudos, o tamanho do arquivo e a frequência com que ele muda preveem problemas de manutenção melhor do que qualquer cheiro isolado.
- O que você sentiria: os cheiros. O catálogo estabelecido de cheiros, cada um traduzido no que contar, quando não é um cheiro, e o playbook de refatorações nomeadas que décadas de prática construíram para ele: referências/cheiros.md.
- O quanto você sentiria: o passo a passo. Pegue a próxima mudança que está por vir, planejada ou provável, e liste tudo o que ela tocaria.
Então o veredito combina onde e o quê:
| Tem cheiro | Não tem cheiro | |
|---|---|---|
| Muda com frequência | Refatore | Diga que está bem |
| Raramente muda | Deixe quieto; no máximo uma nota "Depois" | Nada |
A caixa "Diga que está bem" é tão importante quanto a "Refatore". É a metade da contenção da intuição, e o relatório a mostra.
Dois tamanhos de pergunta. Uma passada cobre o repositório inteiro: etapas 1 a 7. Uma pergunta sobre uma área ("devemos refatorar X?") executa as mesmas etapas apenas nessa área. Ela pula a lista de verificação a menos que haja vários itens, e sua resposta pode ser "não". De qualquer forma, primeiro execute o script da etapa 1 com --checklists sozinho: se ele listar uma lista de verificação de uma passada anterior, leia Após uma passada anterior primeiro.
1. Onde: o mapa de calor
python3 <diretório desta skill>/scripts/heatmap.py -C <repo> <escopo ...>
<diretório desta skill> é a pasta que contém este SKILL.md. Onde python3 não for o comando, como frequentemente no Windows, use python ou py.
- Escopo: as pastas de código-fonte e as pastas de testes, como
src testsouapp, não docs ou assets. Leia o README do projeto, uma listagem de diretórios e quaisquer instruções do agente (AGENTS.md,CLAUDE.md) para decidir. Arquivos de teste, conhecidos pelo nome (Board.spec.ts,test_x.py,XTest.php...) ou pasta, ficam fora da tabela e pares, e são listados separadamente quando mudam com vários arquivos. Adicione--test GLOBpara outros nomes de teste,--not-test GLOBpara um arquivo que só parece ser um teste (o mapa de calor nomeia qualquer um que duvidar), e--ignore GLOBpara arquivos gerados. - Janela: por padrão, os últimos 200 commits.
--sincedefine em vez disso quando o usuário nomeia um ponto de partida ("desde v2.0", "desde que adicionamos cobrança", "os últimos três meses"): uma tag, branch ou commit, ou uma data. Commits de bots, as reformatações em massa que um repositório lista em.git-blame-ignore-revse os commits registrados em listas de verificação anteriores são deixados de fora automaticamente.--exclude-commitsexclui outros que fariam tudo o que tocaram parecer quente, como uma importação de fornecedor. - Muito fino? Com menos de cerca de 30 commits na janela, as contagens não podem distinguir arquivos quentes de frios. Alargue a janela, ou diga que o histórico é muito curto para apontar qualquer lugar e julgue apenas pelos cheiros. Um clone raso (o mapa de calor informa) precisa de
git fetch --unshallow; pergunte ao usuário.
--help explica cada coluna e opção.
Lendo
- Um ponto quente tem muitos commits e também é grande, então a lista os coloca primeiro: é ordenada por commits vezes tamanho. Muitos commits em um arquivo pequeno geralmente é normal, como um config editado para cada funcionalidade. Um arquivo grande que ninguém toca é barato de deixar quieto.
- Ainda quente?
recentconta seus commits na metade mais nova da janela (metade dos commits, não metade do tempo), elasté a data do mais recente. Leia ambos contra as datas da janela: oito commitsrecent, todos no dia em que a metade mais nova começa, são uma explosão que esfriou. Um arquivo que encolheu pode já ter sido tratado. relsão linhas alteradas sobre o tamanho do arquivo. A rotatividade relativa prevê defeitos muito melhor do que a rotatividade absoluta. Muito acima de 1 significa que o arquivo foi reescrito ou cresceu na janela. Leia com crescimento: um arquivo que só cresce, commit após commit, é Crescimento Aditivo-Apenas. Um arquivo nascido na janela tem crescimento igual ao seu tamanho.- Pares são arquivos que mudam juntos nos mesmos commits. Um grupo são três ou mais que todos se emparelham entre si, contados pelos commits que os alteraram a todos; grupos que se sobrepõem são listados juntos como uma área.
- Arquivos de camadas diferentes que se emparelham fortemente, ou um grupo entre camadas, geralmente significam uma funcionalidade espalhada por eles (Cirurgia com Dispersão).
- Verifique o que liga cada par. Uma importação, uma chamada ou um contrato que algo nomeia (uma rota, um esquema, o arquivo de tipo entre um controlador e sua página) é uma dependência visível: uma funcionalidade através de um limite. Arquivos que mudam juntos sem tal ligação, como duas cópias de uma regra ou duas listas mantidas em sincronia manualmente, têm uma dependência oculta, o tipo mais forte de acoplamento.
- Um arquivo que se emparelha com muitos outros é um hub por onde toda mudança passa.
- Um teste listado como mudando com vários arquivos além do seu próprio está vinculado aos seus internos e bloqueará a refatoração, a menos que esses arquivos sejam um grupo que o teste apenas segue.
- Se muitos commits eram grandes demais para emparelhar (
--max-files), como em um repositório que faz squash-merge, aumente-o ou leia os pares como parciais.
- Por que muda. Leia as mensagens de commit dos principais arquivos (
--show) e agrupe-as por funcionalidade. Muitas razões não relacionadas para alterar um arquivo é Mudança Divergente, e isso aparece aqui mais diretamente do que em qualquer contagem.
2. O que: os cheiros
- Meça os arquivos quentes contra o próprio código-fonte, dentro de um idioma.
python3 <diretório desta skill>/scripts/measure.py -C <repo> <escopo ...>coloca cada arquivo em uma faixa pelos percentis 70, 80 e 90 do seu idioma, e--show <arquivo>cada uma de suas funções. Grande é a faixa do 80º ou 90º percentil (as faixas também aplicam o piso do catálogo); o 70º merece uma olhada mais de perto. Onde ele disser que os percentis se apoiam em um arquivo, julgue também pelos pontos de referência em Medindo, que também diz como contar o resto (ramos, parâmetros, coesão), ou usar o linter do projeto. - Leia os dois ou três principais arquivos centrais você mesmo. Números apontam; a leitura decide.
- Revise as outras áreas quentes. Se você pode executar subagentes, dê a dois ou três deles uma área cada (uma camada ou um diretório, para que cada um leia uma área inteira uma vez) e execute-os em paralelo. Instrua cada um a partir de referencias/resumo-revisao.md. Sem subagentes, revise as áreas você mesmo, uma de cada vez, contra o mesmo resumo.
- Nomeie cada cheiro com seu nome estabelecido (Função Longa, Cirurgia com Dispersão, Switches Repetidos...), e verifique-o contra a entrada "Não é um cheiro quando" antes de contá-lo. O nome é o índice para seu playbook.
- Procure a ideia faltante. Cheiros viajam em clusters, e um cluster geralmente aponta para uma coisa que o código usa o tempo todo, mas nunca nomeou: um estado, a definição de um tipo, uma fonte única para uma configuração. Veja Cheiros viajam juntos.
- Verifique quais testes alcançam o código quente: veja Código Não Testado.
- Bugs que você notar vão para a lista como itens F, não corrigidos agora. Um bug ainda não reproduzido é uma suspeita: intitule-o como tal ("Verificar: edições pulam a regra de fim de semana"), com a evidência em sua nota. Seu primeiro passo é um teste com falha; se nenhum falhar, é pulado, com o que você encontrou.
3. Quanto: o passo a passo
Isso é o mais próximo do velho pressentimento. Também é o que torna um item convincente.
- Encontre as próximas mudanças. Uma mudança planejada é melhor que uma inferida, então pegue-as primeiro do que o usuário disse que está por vir ("antes de começarmos no bloco 7"), depois do plano ou roteiro do projeto, se tiver um, depois das mensagens de commit: os tipos de mudança que aparecem com mais frequência ("adicionar um novo tipo de relatório", "adicionar uma configuração", "adicionar uma tela"). Pegue os dois ou três tipos mais comuns, não apenas um.
- Para cada tipo, pegue o commit real mais recente dele e liste o que ele tocou (
git show --stat). Se for mais antigo que a janela, use-o mesmo assim e forneça sua data; o passo a passo, no código de hoje, mostra o que mudou desde então. Se a documentação do projeto ou instruções do agente tiverem uma receita para essa mudança, conte seus passos. Uma receita de cinco passos para adicionar uma coisa é uma admissão. - Percorra a próxima, sem escrevê-la. Liste cada arquivo que você precisaria ler, cada lugar que você editaria e cada cópia que você manteria em sincronia.
- Anote a contagem de hoje e a contagem após a refatoração que você proporia: "um novo tipo de relatório toca 7 arquivos e 3 switches hoje, e tocaria 1 arquivo depois".
Um arquivo frio onde o trabalho planejado vai pousar não é mais frio: coloque-o na linha superior da tabela, já que a próxima mudança paga o que custar. Se a próxima mudança for barata hoje, o cheiro está custando pouco, seja lá como for.
4. Verifique cada descoberta
A descoberta de um revisor é uma pista, não um fato, e o mesmo vale para suas próprias primeiras impressões. Antes de qualquer coisa ir para a lista, abra as linhas citadas, reexecute as contagens e descarte ou corrija o que não se sustentar. Diga no relatório o que você verificou. Um item errado que envia o usuário para uma refatoração custa muito mais do que o minuto que leva para verificá-lo.
Em seguida, aplique a contenção. Um revisor solicitado a encontrar problemas sempre encontrará algo, e corrigir cada descoberta deixa camadas extras e testes para casos que não podem acontecer. Deixe de fora o que é frio, o que as linhas "Não é um cheiro quando" desculpam, e o que o passo a passo diz que é barato.
5. Classifique
Dê a cada item um ID estável, porque o usuário nomeará itens por ele ("começar R4"), possivelmente em uma sessão posterior. Os nomes entre colchetes são os campos do item na lista de verificação (etapa 6); itens F, L e D dizem os seus em uma note.
- R1, R2… Refatorações, na ordem sugerida. Para cada:
- um título nomeando a ideia faltante, como um resultado ("Nomear os estados de um checkout", não "Refatorar CheckoutController") (
title); - os cheiros por nome, com números e
file:line(smells); - o tipo de mudança que facilita, da etapa 3 (
eases), e o custo dessa mudança (cost:todayeafter); - o plano, como os passos do playbook (
plan); - os arquivos, tamanho (P, M ou G) e risco (Baixo, Médio ou Alto) (
files,size,risk); - como verificar que o comportamento não mudou (
verify).
- um título nomeando a ideia faltante, como um resultado ("Nomear os estados de um checkout", não "Refatorar CheckoutController") (
- F1… Pequenas correções encontradas pelo caminho, cada uma seu próprio commit.
- L1… Depois: fedorento, mas frio, ou que vale a pena fazer quando o código próximo for tocado em seguida.
- D1… Decisão do usuário: mudanças em como eles trabalham, como uma convenção de docs que torna toda mudança cara. Apresente a troca, não uma decisão.
Classifique pelo retorno contra o risco. Retorno é o quanto a mudança facilita o trabalho futuro: o mapa de calor e o passo a passo são as evidências. As prioridades dos cheiros dizem quais tipos tendem a importar. Código longo e complexo, e mudança espalhada por arquivos, importam mais. Cheiros de acoplamento como Inveja de Funcionalidade e Homem do Meio importam menos, e são corrigidos de passagem.
Ordene a lista de modo que:
- os bloqueadores venham primeiro: testes vinculados a internos que fariam cada passo posterior parecer que quebrou algo, e código sem cobertura de teste, cujo item começa com fixação de testes ou diz claramente que não pode ser verificado (etapa 7);
- os itens pequenos que removem um caminho de copiar-colar venham em seguida;
- grandes movimentos venham depois, já que frequentemente precisam de uma costura primeiro;
- qualquer coisa que você não possa verificar por si mesmo (como soa, parece ou se sente) venha por último, porque o usuário precisa verificar.
6. Escreva a lista de verificação e o relatório
Execute o comando da etapa 1 novamente com --page .refactor-pass/<AAAA-MM-DD>.html, pela data de hoje. Ele faz a lista de verificação a partir de assets/page.html e escreve o mapa de calor em seu bloco heatmap; execute novamente, ele atualiza o mapa de calor e nada mais. Você escreve o bloco pass abaixo dele, cujo project, started, base e updated já estão preenchidos:
summary: o estado geral em duas ou três frases;items: cada item, com os campos da etapa 5, umstatus(todo,doing,waitingno usuário,done, ouskippedcom o motivo em seuoutcome), ecommitseoutcome, vazios para um item novo; um item transferido mantém seufrom. Cada item começa com seu ID e status em uma linha,{"id": "R1", "status": "todo",, com seus outros campos depois, como o comentário acima dos blocos mostra por completo; não como um formatador JSON bonito o dispõe, já que as atualizações de status da etapa 7 editam essa linha única;left_alone: cada um comwhatewhy;checks: as verificações do projeto, de suas instruções, ou o que você verificará manualmente se não tiver nenhuma;earlier: após uma passada anterior, seuchecklisteresults, um por refatoração concluída nele: seuid,title,easesecost, achangereal que veio em seguida e o que elaactualmente tocou (ambos vazios se nenhum veio ainda), e umanote. Caso contrário,null;log: cada um com umadateetext, mais recente primeiro.
Após cada edição, execute python3 <diretório desta skill>/scripts/check_page.py <página>, que captura um colchete quebrado, um campo com erro de ortografia, um ID duplicado ou um item que não começa em uma linha. Se seu ambiente puder publicar ou pré-visualizar páginas HTML, p
[...]