Como usar os critérios de Robert C. Martin sem transformar revisão em disputa estética

Uma revisão de código pode consumir uma tarde discutindo o tamanho de uma função e deixar passar uma cobrança duplicada. Também pode aceitar um método de duzentas linhas porque todos já se acostumaram com ele. As duas situações expõem a dificuldade de explicar o que torna um código fácil de manter. É nesse espaço que Clean Code continua sendo uma leitura útil: ele oferece assuntos concretos para discutir, embora algumas de suas recomendações precisem de bastante julgamento.
A pergunta que proponho para acompanhar a leitura é simples: depois da alteração, alguém consegue localizar uma regra, antecipar seus efeitos e verificar seu comportamento com menos esforço? Esse critério permite aproveitar o livro e discordar dele com argumentos verificáveis. Também evita transformar a opinião de um autor em regra automática de aprovação de PR.
Esta resenha trata de Clean Code: A Handbook of Agile Software Craftsmanship, de Robert C. Martin, primeira edição, publicada em agosto de 2008, ISBN 9780132350884. O catálogo registra copyright de 2009; são informações compatíveis sobre a mesma edição. A organização combina princípios, estudos de código, incluindo JUnit e SerialDate, e heurísticas. Esta análise se limita à primeira edição. A referência bibliográfica é a página da editora Pearson/InformIT.
O livro examina nomes, funções, comentários, tratamento de erros e testes como escolhas que afetam a manutenção. Minha recomendação é lê-lo para argumentar sobre legibilidade com critérios concretos e avaliar se uma refatoração facilita a próxima mudança. Para alguém que já programa, mas ainda comenta revisões com "está confuso", isso representa um ganho: a impressão começa a virar uma hipótese que pode ser discutida.
A preocupação com o custo acumulado da desorganização aparece na amostra oficial da primeira edição. Minha leitura desse argumento é que cada decisão local deixa uma conta para quem modifica o programa depois. Uma variável ambígua exige investigação; uma função que mistura cálculo e rede amplia os cenários necessários para testar uma regra; uma convenção inconsistente obriga a consultar o histórico.
O mérito dessa perspectiva está em devolver importância ao trabalho cotidiano. Arquitetura não torna irrelevante um nome que omite unidade de medida. Um serviço pode ter limites organizados e ainda conter um cálculo impossível de conferir. Ao mesmo tempo, o esforço deve acompanhar a consequência: renomear uma variável privada custa pouco; alterar uma API pública usada por clientes exige compatibilidade, migração e comunicação.
Por isso, eu usaria o livro como um repertório para formular perguntas de revisão. Uma lista de proibições produz respostas rápidas, mas pouco contexto. Perguntar qual informação o leitor precisa reconstruir costuma levar a uma discussão melhor sobre o código que realmente está na tela.
Compare amount, total e subtotal_cents. O último nome explicita uma unidade e uma etapa do cálculo. Ainda faltam perguntas sobre impostos e moeda, mas uma ambiguidade desapareceu sem criar uma classe ou mover arquivos. Esse tipo de melhoria tem boa relação entre esforço e benefício.
Há um limite importante: um nome comprido pode narrar uma implementação incompreensível. validateAndReserveAndChargeAndNotify revela os efeitos, porém também sinaliza que o chamador talvez precise de operações com fronteiras diferentes. E vocabulário exige conhecimento do negócio. Cancelar uma autorização, devolver um pagamento e cancelar um pedido são eventos diferentes; padronizar tudo como cancel pode esconder essa distinção.
O mesmo cuidado vale para parâmetros. No artigo do próprio Martin sobre argumentos booleanos, ele defende eliminá-los por esconderem comportamentos distintos. submit(order, True) demonstra o problema: o leitor precisa descobrir o significado do sinalizador. Separar preview_order de place_order torna uma diferença de efeito visível no ponto da chamada.
Eu aplicaria esse raciocínio a seletores de modo com significado obscuro. Um booleano que representa um fato do domínio, como a confirmação de consentimento, merece outra análise. Trocar todo booleano por métodos adicionais pode aumentar a API sem esclarecer o modelo. A pergunta útil é qual distinção o chamador precisa compreender e quais combinações a interface permite por engano.
Produtos gratuitos e pagos para transformar ideias em uma base que você consegue executar.
13 produtos disponíveisContinue explorando tópicos similares

Resenha crítica de The Clean Coder: estimativas, negociação, testes e responsabilidade, com um caso concreto e limites para cobranças individuais.

Resenha da edição de vinte anos de The Pragmatic Programmer: DRY, reversibilidade, protótipos e feedback aplicados a uma importação de estoque.

Resenha de Refactoring, segunda edição: passos pequenos, testes e contratos, com exemplo executável, críticas e um roteiro de aplicação.
Checklist de 47 pontos para encontrar bugs, riscos de segurança e problemas de performance antes do lançamento.
Templates testados em produção, usados por desenvolvedores. Economize semanas de setup no seu próximo projeto.
O exemplo abaixo é original e didático, não foi retirado do livro. Considere uma loja fictícia: um pedido com subtotal de pelo menos 20.000 centavos recebe frete gratuito; abaixo disso, o frete custa 1.500 centavos. O total resulta da soma dos dois valores. Não há descontos, impostos ou integração externa nesse recorte.
Uma primeira implementação pode concentrar tudo em process(order): somar itens, verificar o limite, cobrar e enviar email. Extrair o cálculo cria uma fronteira útil porque permite discutir a regra sem executar uma cobrança. A versão mínima abaixo usa Python 3, sem dependências. Ao executá-la, todas as verificações passam e o programa termina sem saída:
FREE_SHIPPING_THRESHOLD_CENTS = 20_000
STANDARD_SHIPPING_CENTS = 1_500
def shipping_cents(subtotal_cents: int) -> int:
if subtotal_cents < 0:
raise ValueError("subtotal must be nonnegative")
if subtotal_cents >= FREE_SHIPPING_THRESHOLD_CENTS:
return 0
return STANDARD_SHIPPING_CENTS
def total_cents(subtotal_cents: int) -> int:
return subtotal_cents + shipping_cents(subtotal_cents)
assert total_cents(0) == 1_500
assert total_cents(19_999) == 21_499
assert total_cents(20_000) == 20_000
assert total_cents(25_000) == 25_000
try:
total_cents(-1)
except ValueError:
pass
else:
raise AssertionError("negative subtotal was accepted")
O caso 19.999 torna o limite observável. Testar apenas um pedido muito barato e outro muito caro deixaria a comparação > versus >= sem proteção direta. Já o caso zero registra uma decisão potencialmente estranha: um pedido vazio ainda paga frete. O código reproduz a regra anunciada; a equipe precisaria perguntar ao responsável pelo produto se esse comportamento faz sentido antes de colocá-lo em produção.
A extração vale porque shipping_cents apresenta um conceito que pode ser explicado de forma independente. Criar ainda is_high_enough, get_standard_value e add_values fragmentaria um cálculo que já cabe na memória de quem lê. As linhas seriam menores, mas a navegação aumentaria. Minha preferência aqui é manter a comparação e seu resultado próximos.
As asserções verificam somente esses exemplos. Elas não demonstram equivalência com um checkout existente, não validam tipos em tempo de execução e não modelam múltiplas moedas. Também não resolvem idempotência, preço alterado durante a compra, persistência ou falhas do provedor de pagamento. O ganho de clareza é local e deve ser descrito assim. Em uma refatoração real, seria necessário proteger o comportamento anterior antes de substituir o cálculo.
Um exemplo organizado tampouco resolve sozinho uma discussão arquitetural. O mesmo cálculo pode precisar de outra fronteira quando várias políticas de preço mudam independentemente ou quando um contrato público precisa manter compatibilidade. A mudança necessária no sistema deve orientar os pontos de extensão, em vez de requisitos hipotéticos.
Um comentário como "soma o frete ao subtotal" adiciona pouco ao retorno de total_cents. Um comentário que registra a razão comercial do limite, com referência à decisão correspondente, pode evitar que alguém o "corrija" meses depois. A distinção depende da informação que o código consegue expressar. Nomes descrevem operações; nem sempre registram por que uma restrição existe.
Eu teria especial cuidado com protocolos, algoritmos e compatibilidade. Remover uma nota sobre um comportamento defeituoso de um fornecedor pode apagar a justificativa de uma condição aparentemente redundante. O custo de manter o comentário deve ser comparado ao custo de redescobrir essa decisão. Documentação que descreve contrato público também atende a quem não deve precisar ler a implementação.
Os testes, por sua vez, ajudam a tornar explícito o que a revisão deseja preservar. No exemplo, os nomes revelam as unidades, enquanto os casos revelam o limite. Se cada auxiliar privado recebesse seu próprio teste rígido, mudar a decomposição poderia quebrar a suíte sem alterar o resultado. Eu privilegiaria contratos observáveis e reservaria testes internos para lógica que justifique uma verificação independente.
O ponto mais discutível é transformar tamanho de função em substituto de simplicidade. Uma rotina curta pode depender de vários conceitos externos; uma rotina um pouco maior pode apresentar uma sequência coesa. Meu critério seria observar quantos detalhes o leitor precisa carregar para fazer uma alteração específica. Contar linhas não resolve essa avaliação.
Essa discordância tem um interlocutor identificável. John Ousterhout declara diferenças com Martin sobre extensão de métodos e comentários na página de A Philosophy of Software Design. Eu recomendo confrontar as duas perspectivas usando o mesmo trecho do seu projeto. Se uma extração acrescenta uma interface e deixa o chamador dependente de todos os detalhes internos, a melhoria precisa ser demonstrada.
Há também o contexto de uma obra de 2008, fortemente associada a exemplos de orientação a objetos e Java. Transportar sua organização para código funcional, pipelines de dados ou uma rotina com requisitos de desempenho exige adaptação. Objetos extras e chamadas adicionais podem ser adequados em um serviço e inconvenientes em outro contexto. Qualquer afirmação sobre desempenho pede medição representativa; o livro não dispensa esse trabalho.
Outra limitação da recomendação indiscriminada é o momento da equipe. Um sistema sem testes, com entrega urgente e comportamento desconhecido, precisa de uma estratégia segura de mudança. Uma limpeza ampla pode consumir a janela de entrega e introduzir regressões. Melhor escolher um ponto necessário à alteração atual, registrar o contrato e manter o diff revisável. Código limpo não autoriza reescrita ilimitada.
Recomendo a leitura para quem já consegue implementar funções, entender chamadas e escrever testes simples, mas encontra dificuldade para explicar decisões em revisão. Um projeto pequeno existente é suficiente para acompanhar os argumentos; experiência com incidentes ajuda a reconhecer efeitos escondidos. A senioridade importa menos que ter código concreto para questionar.
Para quem ainda está aprendendo sintaxe e depuração, eu adiaria uma leitura integral. Praticar programas pequenos e testes torna a discussão menos abstrata. Para pessoas experientes, o retorno tende a estar na revisão dos critérios da equipe: quais regras realmente reduzem erros, quais apenas reproduzem o gosto de alguém e quais ignoram as particularidades da linguagem?
Na série, minha ordem sugerida depende da dificuldade atual. Se você concorda com o objetivo de melhorar o código, mas não sabe conduzir mudanças pequenas, avance para Refactoring. Se o principal obstáculo é alterar código sem proteção, priorize Working Effectively with Legacy Code. Para questionar se a decomposição reduziu complexidade, leia A Philosophy of Software Design. São funções diferentes na sua formação, e as comparações ajudam a evitar uma preferência única para todo problema.
Leia um tema por vez e mantenha um registro com quatro campos: trecho problemático, dúvida do leitor, mudança proposta e evidência esperada. Comece por um nome ambíguo em código que você já precisa alterar. Depois experimente separar um cálculo de um efeito externo. Essa sequência permite perceber benefícios e custos antes de propor uma convenção para o repositório inteiro.
Em cada experimento, peça a outra pessoa que descreva entradas, saídas e efeitos antes e depois. Verifique os mesmos casos de comportamento, incluindo limites e erros relevantes. Registre também o que piorou: mais navegação, interface adicional ou dificuldade para localizar a regra. Um relato honesto desses custos vale mais que declarar o trecho "limpo".
Meu veredito é favorável com esse método de leitura. Clean Code oferece um vocabulário fértil para discutir manutenção e incentiva atenção a decisões pequenas que acumulam custo. Sua utilidade diminui quando heurísticas viram ordens sem contexto. Vale ler com um editor e um projeto abertos, pronto para explicar tanto uma melhoria quanto uma discordância. O resultado desejável é uma equipe capaz de justificar mudanças com mais precisão.