O que Revisão de Qualidade faz?
Alvo: $ARGUMENTS
Revise o alvo quanto à qualidade de design e testes. Relate descobertas; não edite código.
1. Carregar a rubrica
Leia ambos antes de olhar para o código:
~/.claude/CODING_STANDARDS.mdé a rubrica. Cada descoberta cita um de seus títulos de seção.- design-vocabulary.md contém os termos para formular as descobertas e os testes usados para verificá-los abaixo.
Leia também o AGENTS.md / CLAUDE.md do repositório, se presente. Onde esses arquivos dizem que o repositório faz algo deliberadamente que os padrões desencorajam, diga isso uma vez no relatório, em vez de sinalizar cada instância. Um hábito que você apenas observa no código (todo módulo lê o ambiente, todo teste faz monkeypatch) não é uma escolha documentada: novo código que o segue ainda é uma descoberta.
2. Resolver o alvo
- Nenhum argumento: a branch atual contra sua base.
git merge-base HEAD origin/main(oumaster), entãogit diff <base>...HEADmais alterações não commitadas degit diff HEAD. - Número de PR ou URL:
gh pr diff <n>egh pr view <n> --json baseRefName,headRefName,files. - Branch:
git diff <base>...<branch>. - Caminho, ou
.para o repositório inteiro: revise o código existente sob ele como está, sem diff. Isso é modo codebase, abaixo.
3. Leia o suficiente para julgar design
Modo de mudança (diff, branch, PR)
Um diff sozinho não pode mostrar se um módulo é profundo. Para cada módulo alterado, leia o arquivo inteiro e procure por seus chamadores e seus testes. Profundidade, posse e duplicação são julgamentos sobre os locais de chamada, então olhe para eles.
Escopo das descobertas para o que a mudança introduz ou piora. Um problema pré-existente no código que o diff toca está no escopo se a mudança foi o momento natural para corrigi-lo; um em código não tocado não está.
Novos módulos recebem revisão em escala de módulo, como no modo codebase: percorra Tipos e Legibilidade tão completamente quanto as outras seções. Para cada módulo novo ou substancialmente reescrito, liste cada função com seus tipos de parâmetro e retorno antes de julgá-lo, e observe cada mapa, dict ou tupla não tipada que ele aceita, retorna ou lê chaves, e cada nome de caractere único.
Meça a complexidade ciclomática para cada arquivo fonte alterado antes de julgar Legibilidade, com a ferramenta usual da linguagem: por exemplo radon cc -s para Python, gocyclo para Go, a regra complexity do eslint para JavaScript/TypeScript, cognitive_complexity do clippy para Rust, PMD para Java. Execute-o através da própria ferramenta do repositório, se houver, ou um executor único (uvx, npx, go run) se não. Prefira uma ferramenta que conte operandos booleanos de curto-circuito; algumas (C901 do ruff, por exemplo) os ignoram e subestimam as piores funções. Sem ferramenta disponível, conte os pontos de decisão manualmente como a rubrica os define. Uma função que o diff adiciona com pontuação acima de 10, ou uma existente que o diff empurra para além de 10 ou aumenta ainda mais, é candidata. Para uma função existente, pontue a versão base também (escreva git show <base>:<file> em um arquivo temporário e meça isso) para que a descoberta possa dizer o que a mudança adicionou.
Quando o diff adiciona mais de cerca de 150 linhas de código fonte, lance dois subagentes general-purpose em paralelo enquanto você revisa as seções de design, cada um recebendo o diff, a lista de arquivos alterados e as regras de verificação do passo 4 para suas seções: um para Tipos, Legibilidade e "Prefira dados imutáveis onde valer a pena", o outro para Testes, aplicando as verificações de asserção a cada novo teste. Verifique o que cada um retorna antes de mantê-lo.
Modo codebase (caminho ou .)
Tudo sob o caminho está no escopo, mas o nível para uma descoberta aumenta com o tamanho do escopo. Um único módulo ou diretório pequeno (aproximadamente abaixo de 1.500 linhas de código fonte) recebe descobertas em nível de módulo, função e nomenclatura: percorra Tipos e Legibilidade tão completamente quanto as outras seções. Um pacote ou o repositório inteiro recebe apenas os problemas de alto impacto: as decisões de design e padrões arquiteturais que seguram a base de código. O mesmo se aplica quando o usuário nomeia um recurso ou ferramenta (por exemplo, "toda a CLI") que acaba sendo um módulo: julgue-o em escala de módulo. Pergunte de cada candidato em escala de pacote ou repositório:
- Quantos módulos, testes ou mudanças futuras pagam por ele? Um custo confinado a uma função é um detalhe menor nesta escala.
- Consertá-lo mudaria como novo código neste repositório é escrito? Se a correção são algumas linhas locais sem efeito sobre os chamadores, descarte-a.
- É um padrão ou um caso isolado? Instâncias individuais contam apenas como evidência para um padrão.
Exemplos de problemas de alto impacto: um sistema externo sem módulo proprietário, então todo chamador refaz autenticação e paginação; política e orquestração fundidas nos entrypoints, então decisões só podem ser testadas ponta a ponta; posse de estado dividida entre módulos que cada um conhece o layout de armazenamento; clientes e configuração construídos no momento da importação, então nada pode ser testado sem o ambiente real; uma suíte de testes que aplica patch em partes internas porque não existem costuras. Uma função longa, uma variável mal nomeada, uma única exceção engolida ou uma asserção fraca não é de alto impacto por si só.
Esses problemas abrangem módulos, então construa um mapa antes de ler arquivos em profundidade:
- os módulos (arquivos ou pacotes) e aproximadamente o que cada um faz, de
git ls-files <path>e uma olhada no topo de cada módulo - como as peças principais se conectam: o que chama o que, e como estado e dados se movem entre entrypoints, módulos e sistemas externos
- cada sistema externo (API, banco de dados, fila, formato de arquivo, modelo) e todo módulo que fala diretamente com ele
- todo lugar onde ambiente ou configuração é lido (
os.environ,getenv,process.env, análise de flags) - estado mutável em nível de módulo e efeitos no momento da importação (clientes, conexões, caches, locks construídos no escopo do módulo)
- os entrypoints, e o diretório de testes com quais módulos cada arquivo de teste exercita
Grep é suficiente para a maior parte disso. Mantenha o mapa curto; é um índice de trabalho, não parte do relatório.
Se o escopo for pequeno (aproximadamente abaixo de 3.000 linhas de código fonte), revise-o diretamente do mapa, e em paralelo lance dois subagentes general-purpose, cada um recebendo a lista de arquivos e as regras de verificação do passo 4 para suas seções. Um recebe apenas as seções Tipos, Legibilidade e "Prefira dados imutáveis onde valer a pena" da rubrica. O outro recebe apenas a seção Testes e deve aplicar as verificações de asserção a cada teste. Dessa forma, essas seções recebem atenção total em vez do que sobra após as anteriores. Verifique o que cada um retorna antes de mantê-lo. Caso contrário, divida-o em áreas de módulos relacionados, cada uma com seus testes, e dê cada área a um subagente general-purpose, lançado em paralelo. Dê a cada um o caminho da rubrica, o caminho do vocabulário, o mapa, sua lista de arquivos, as regras de verificação do passo 4 e o nível de alto impacto acima, e diga-lhe para não editar nada. Um subagente que vê apenas sua própria área tende a detalhes locais, então peça-lhe os padrões de design que vê em sua área e a evidência para cada um (arquivo:linha), não uma lista de descobertas em nível de linha. Problemas entre módulos vêm do mapa, então elabore-os você mesmo, depois combine-os com os padrões dos subagentes, mescle aqueles que são a mesma decisão subjacente vista de diferentes áreas, e re-verifique a evidência para cada um antes de relatar.
4. Revisar contra cada seção
Percorra os padrões em ordem: Módulos, Estado, Tipos, Dependências e configuração, Erros, Legibilidade, Testes. Para cada candidato a descoberta, verifique-o antes de mantê-lo:
- Módulo raso ou pass-through: aplique o teste de exclusão. Nomeie os chamadores e diga qual complexidade reapareceria neles, ou mostre que nada aconteceria.
- Abstração que não ganhou seu lugar: conte os adaptadores na costura. Um fake de teste conta. Um adaptador com nada escondido atrás dele é uma descoberta; dois é uma costura real.
- Abstração faltando: verifique a direção oposta com a mesma intensidade. Procure por uma preocupação tratada inline em três ou mais lugares, dentro de um módulo ou entre módulos.
- Proprietário faltando / preocupação duplicada: liste cada lugar onde a preocupação é escrita (arquivo:linha), não "isso aparece em vários lugares".
- Conceito sem proprietário: liste cada lugar onde é representado, analisado ou construído (arquivo:linha), e qualquer assinatura onde parâmetros com o mesmo tipo com significados diferentes estão lado a lado. Identificadores contam como conceitos, não apenas dados estruturados. Nomes e IDs de diferentes tipos passados como a mesma primitiva (um nome de repositório fonte e alvo, um nome de branch e uma referência, dois tipos de ID) são o principal caso a pegar. Então diga que tipo de proprietário se encaixa. Se os chamadores seguram e passam o conceito, recomende um tipo para ele. Se eles só precisam que algo seja feito com ele, recomende escondê-lo dentro do módulo que faz o trabalho. Não coloque um tipo de domínio por padrão.
- Restrições de ordenação ou chaves/caminhos/IDs construídos manualmente: mostre o chamador fazendo o sequenciamento ou a construção.
- Estado mutável compartilhado, efeitos no tempo de carga, configuração lida profundamente: nomeie o global ou a chamada no momento da importação, e o teste ou segunda configuração que ele bloqueia.
- Escritas de snapshot: mostre a leitura, a etapa de longa duração e a escrita de volta.
- Dados mutáveis que seriam melhores imutáveis: relate apenas quando a mutabilidade tem um custo concreto. Mostre o segundo detentor que vê uma mudança inesperada, o cache ou chave que torna inseguro, ou a mudança feita longe de onde o valor foi criado. Uma estrutura mutável com um proprietário que a muda através de seu próprio código não é uma descoberta. Se a versão imutável custaria desempenho, diga o que memorizar ou compartilhar, e onde esse cache vive.
- Estado inválido que pode ser representado: nomeie o estado que nunca deve acontecer e mostre o tipo que o permite (campos opcionais que devem ser definidos juntos, combinações de booleanos, uma string com alguns valores permitidos). Liste as proteções ou verificações de
Noneque existem apenas por causa disso (arquivo:linha). Proponha a forma que torna isso impossível, e diga quais proteções essa mudança deleta. - Dict ou tupla como um conceito: liste as chaves lidas e cada função que as lê (arquivo:linha), e cada lugar onde o dict é construído. Proponha o tipo de registro no estilo existente do repositório (um modelo pydantic ou dataclass, uma interface TypeScript, um struct Go, o que o repositório já usa), mantendo apenas os campos que o código usa, e diga onde o payload é analisado nele.
- Módulo que possui um sistema externo: verifique se suas funções compartilham uma credencial, identidade, URL base ou cache através de globais do módulo ou leituras de ambiente por chamada, e se os testes aplicam patch em várias de suas funções. Se sim, recomende uma classe de cliente com métodos nomeados no domínio, construída na inicialização e passada, e nomeie os patches que o fake substituiria.
- Comentários de doc faltando: liste cada função não trivial sem um (arquivo:linha) e o que seu comentário de doc precisaria dizer que a assinatura não diz (um fallback, um modo de erro, estado que toca, uma forma de entrada). Agrupe-os em uma descoberta de Legibilidade por módulo.
- Nomes de caractere único: relate-os como uma descoberta de Legibilidade agrupada listando cada um (arquivo:linha) com um nome proposto. Eles contam para o limite de Legibilidade como uma descoberta, nunca como uma razão para descartá-los.
- Erros engolidos: mostre o caminho da falha para um resultado que muda enquanto apenas uma linha de log o registra.
- Nome com vários significados: faça grep e liste o que ele contém em cada local.
- Nome que esconde seu nível ou objeto: mostre a cadeia de chamadas ou locais de chamada onde lê ambiguamente, e proponha a substituição.
- Função que faz mais de uma coisa: tente escrever seu resumo de uma frase. Se não conseguir, cite a ramificação, bloco try ou parâmetro de modo que o impede.
- Função muito complexa: para cada candidato da medição de complexidade, dê sua pontuação, então liste as fases que executa em ordem (filtre a entrada, busque, reivindique, valide, construa, crie, registre, notifique, ...) com seus intervalos de linha e a regra de falha de cada fase. Proponha a divisão como funções nomeadas, uma por fase ou decisão, com assinaturas, e diga o que cada peça extraída torna testável sem patching; geralmente as decisões se tornam funções puras e o original lê como uma sequência curta de etapas nomeadas. Uma descoberta por função. Acima de 20, ela se classifica com as descobertas de design; 11 a 20 é uma descoberta de Legibilidade que não conta para o limite de Legibilidade. Não descarte uma divisão porque a função é um entrypoint ou porque o repositório tem funções maiores.
- Asserções de teste: nomeie o bug do produto que faria a asserção falhar. Se não puder, essa é a descoberta. Para casos parametrizados ou orientados por tabela, trace cada entrada através do código e diga qual parte dela atinge o comportamento para o qual o caso é nomeado. Um caso cuja entrada distintiva é consumida por uma ramificação anterior não cobre esse comportamento, mesmo que um teste para ele exista. Da mesma forma, verifique se a fixture ou fake pode realmente produzir a entrada que dispararia cada caminho de proteção ou recusa. Se nunca puder, a proteção não foi testada.
- Dublês de teste: diga sobre o que é o teste, e então se o dublê está fora desse limite ou reimplementa lógica do produto.
- Testes que aplicam patch em partes internas: aponte para a decisão que deve ser extraída ou a dependência que deve ser passada, já que essa é a correção real.
- Costura ou porta proposta, em qualquer descoberta de correção: dê a ela operações nomeadas no domínio (
close_ticket(id)), não verbos de transporte (get/post/put). Uma porta que apenas encaminha solicitações falha no teste de exclusão, e seu fake teria que reimplementar o roteamento. Verifique cada costura proposta contra o teste de exclusão antes de relatá-la.
Dê a Testes a mesma profundidade das seções anteriores, mesmo quando as seções anteriores produzem muitas descobertas. Execute cada verificação de Testes acima contra cada arquivo de teste: para cada asserção, nomeie o bug do produto que a faria falhar. Um comportamento relevante para segurança sem asserção de falha (redação, uma verificação de autenticação, um portão de aprovação) se classifica com as descobertas de design.
Descarte qualquer coisa que você não possa tornar concreta. Descarte detalhes de estilo que os padrões não cobrem. Não relate bugs de corretude aqui, a menos que sigam diretamente de um problema de qualidade (por exemplo, uma escrita de snapshot que perde dados); mencione-os em uma linha e aponte para /code-review