Refatorar 40 linhas em 14 funções pode dobrar tempo de onboarding e travar o time. O dogma do Clean Code tem ressalvas que checklists apagaram.
A história da review com o júnior
Recebi um PR de um engenheiro júnior. Quarenta linhas de regra de negócio, legíveis, com três ramificações, dois comentários honestos sobre “caso especial” e um teste cobrindo o caminho feliz.
Devolvi pedindo refatoração. “Uma função por responsabilidade, por favor.” O júnior voltou três dias depois com 14 funções espalhadas por 3 arquivos, 2 classes auxiliares, 1 interface que ainda não tinha implementação, e uma pasta utils/ nova. A complexidade ciclomática caiu. As linhas de cobertura, não. O tempo de onboarding do próximo dev, que até ali era “abre o arquivo e lê 40 linhas”, virou “abre 3 arquivos, segue 14 chamadas e descobre qual tem a regra de negócio de verdade”.
Foi nesse momento que parei de tratar Clean Code como checklist.
O que Clean Code defende de verdade (e o que virou dogma)
O capítulo 3 do Clean Code, do Uncle Bob, tem ressalvas. Ele diz: “funções devem ser curtas” e em seguida exemplifica limites com código real, mostrando que a regra é heurística, não mandamento. A frase de cabeçalho virou meme. As ressalvas não viralizaram.
O que pegou foi o checklist: “função menor que 20 linhas, uma responsabilidade por função, máximo de três parâmetros, zero efeitos colaterais”. Aplicado de forma religiosa, o checklist transforma código simples em arquitetura que parece mais sólida que o problema que ela resolve.
Defender o livro aqui seria injusto. O texto tem nuance que a cultura engoliu inteira e digeriu mal.
O caso: antes e depois lado a lado
A tabela que abro abaixo descreve exatamente o PR que recebi. Os números de tempo de onboarding são estimativas com três devs do time, cronometrados informalmente, mas o padrão é consistente: dobrou.
| Métrica | Antes (1 função, 40 linhas) | Depois (14 funções, 3 arquivos) |
|---|---|---|
| Linhas totais de código | 40 | 112 (com boilerplate) |
| Complexidade ciclomática média | 6 | 2 |
| Arquivos a abrir pra entender o fluxo | 1 | 3 |
| Funções chamadas pra executar o caso feliz | 1 | 6 indiretas |
| Tempo estimado de onboarding (3 devs) | ~5 min | ~12 min |
| Cobertura de teste | 1 caminho feliz | 1 caminho feliz, 2 ramos extras |
Olha o que aconteceu. A métrica de “qualidade interna” melhorou. A métrica de “velocidade de entender o sistema” piorou. E nenhuma ferramenta automática avisa a segunda, porque ela não cabe em número de classe.
Voltei a abrir o chat com ele três meses depois. O sistema tinha virado monolito de utilidades. Cada novo caso de uso ia pra um arquivo novo com sufixo _service, _handler ou _processor. A pasta utils/ que ele criou no PR original tinha crescido pra 40 arquivos. O lead time do time tinha subido de 2 dias pra 9. E ninguém queria mexer no módulo porque “é cheio de regras escondidas”.
O que começou como 40 linhas honestas virou um sistema que precisa de diagrama pra ser explicado. Isso é dívida técnica de verdade. Não a que o SonarQube aponta. A que você sente quando tenta entender o PR da feature seguinte.
A citação que não viralizou
O capítulo 3 do Clean Code abre com a frase que virou meme — “a primeira regra de funções é que elas devem ser pequenas. A segunda é que elas devem ser menores que isso” — e em seguida dedica os parágrafos seguintes a qualificar. Uncle Bob argumenta que funções longas são sintoma de código mal estruturado, mas também que “a função ideal tem entre 4 e 20 linhas, embora funções de 100 linhas em código puramente declarativo não sejam absurdas”. A frase de 4 a 20 linhas virou cartão de visita. A ressalva de 100 linhas em código declarativo foi pro lixo da cultura.
Não é má-fé de quem leu. É o modo como a Internet funciona: manchete vende, contexto não. Você cita o número, ganha retweet. Você cita a ressalva, perde audiência. Depois de dez anos, sobra só a manchete.
Métrica alternativa: dor de cabeça
Se você só mede coesão, acoplamento, complexidade ciclomática, lines of code por função e número de métodos por classe, você está medindo a facilidade de mexer no código. Não a facilidade de entregar com o código.
As métricas que pagam a conta são outras:
- Lead time — do commit ao deploy em produção.
- PR-to-merge — quanto tempo o PR fica aberto esperando review ou retrabalho.
- Taxa de bugs pós-deploy — especialmente bugs recorrentes na mesma região do código.
- Tempo de onboarding — quanto um dev novo leva pra fazer o primeiro PR com confiança no módulo.
Coesão e acoplamento são diagnóstico, não meta. Eles te dizem onde mexer quando o lead time explode. Não te dizem que mexer.
Código que vale: quando quebrar a regra é a coisa certa
Um exemplo real, simplificado, em Python. O lado “limpo” aplica o checklist de forma religiosa. O lado “feio” faz a mesma coisa em metade do espaço, sem abstração vazia.
O lado “limpo” (40 linhas, 6 funções)
class OrderValidator:
def __init__(self, rules): self.rules = rules
def validate(self, order): return self._check_all(order, self.rules)
def _check_all(self, order, rules):
for rule in rules:
if not self._apply_rule(order, rule): return False, rule.name
return True, None
def _apply_rule(self, order, rule): return rule.matches(order)
def _log_failure(self, rule_name, order):
logger.warning(f"Order {order.id} failed rule {rule_name}")
return False
def process_order(order, validator, repo, notifier):
ok, failed = validator.validate(order)
if not ok: return validator._log_failure(failed, order)
repo.save(order)
notifier.send_confirmation(order)
return True
O lado “feio” (12 linhas, 1 função)
def process_order(order, validator, repo, notifier):
for rule in validator.rules:
if not rule.matches(order):
logger.warning(f"Order {order.id} failed rule {rule.name}")
return False
repo.save(order)
notifier.send_confirmation(order)
return True
O lado “limpo” tem mais métodos, mais arquivos mentalmente, mais indireção. Pra entender o que process_order faz, você pula por 5 funções. O lado “feio” mostra o que faz, na ordem que faz, sem te fazer pular. São 12 linhas. Qual você prefere debugar às 23h em produção?
Esse é o ponto. O checklist de complexidade ciclomática vai te dizer que o lado “limpo” é melhor. A pergunta útil é: pra quem vai ler isso daqui a seis meses? Pra quem está sendo pago pra entregar, ou pra quem está sendo pago pra impressionar o SonarQube?
Quando refatorar, então?
Três critérios pragmáticos, nessa ordem. Se nenhum dos três bate, deixa o código quieto.
- O mesmo trecho já foi tocado três vezes em seis meses. Não é “eu acho que vai dar problema”. É histórico. Se você refatora sem histórico, está apostando.
- O bug recorrente vive ali. Mesmo trecho, mesmo tipo de erro, terceiro PR de correção. Refatorar para eliminar a categoria de bug vale o investimento.
- A função tem ramificações suficientes pra travar o teste mental. Não “passou de 20 linhas” — isso é vaidade. “Eu não consigo ler e prever o resultado sem rodar” — isso é limite cognitivo real.
Fora disso, código feio que funciona é melhor que código bonito que ninguém entende.
O caso Posseidom: 14 meses de refatoração contínua
Na Posseidom, sistema interno de automação comercial que mantenho, herdei em 2024 um módulo de conciliação financeira escrito “à moda antiga”: 1 arquivo, 280 linhas, função única, três ramificações grandes, zero testes. O score de qualidade estática era 35/100. O número de bugs em produção por mês era zero.
Novo engenheiro entrou no time em janeiro. Leu o módulo em 4 horas. Propôs refatorar pra 11 funções seguindo o checklist. Pedi pra ele esperar. Esperei seis meses. Os bugs em produção continuaram zero. O time não tocou o módulo. Em julho, entrou a primeira feature nova que dependia daquela lógica. O engenheiro escreveu a feature em 2 dias, sem mexer no módulo original. Adicionou 40 linhas novas, dois testes. Sumiu.
No acumulado: 14 meses, zero bugs, uma feature adicionada, zero horas gastas com refatoração preventiva. Se eu tivesse aceitado a refatoração em janeiro, o engenheiro teria gasto duas semanas, gerado 300 linhas novas de código, e o módulo estaria agora com 580 linhas espalhadas por 4 arquivos pra fazer o que 280 linhas faziam. E a feature de julho? Teria levado 6 dias, não 2, porque o engenheiro teria que reaprender a regra de negócio agora codificada em 11 funções.
Isso é o que o dogma custa. Não custa em linhas. Custa em horas de engenheiro que foram gastas lendo código que não precisava existir.
Por que esse dogma pegou
Três forças sociais, nenhuma técnica.
- Cultura de PR-approval. Review virou gate. Parecer rigoroso na review é mais importante do que aprovar o que funciona. Função grande = reprovação fácil.
- Ferramentas de análise estática que viraram nota. SonarQube, CodeClimate, e similares dão score. Score virou KPI. KPI virou carreira. Engenheiro que sobe o score ganha visibilidade; engenheiro que entrega rápido sem mexer no score não.
- Ansiedade de júnior em parecer rigoroso. O júnior do meu exemplo queria aprender. Adotou o checklist que parecia “profissional” e reproduziu a forma sem entender o trade-off. Isso não é falha do júnior. É falha do código cultural que ele copiou.
O problema é social, não técnico. Tratar como técnico (mais checklist, mais ferramenta, mais regra) só piora a dinâmica.
O que fazer com a review hoje, se você é lead
Três ações práticas pra reverter a dinâmica sem virar o “anti-checklist” (que seria só mais um dogma):
- Elimine o veto por estética. Tire do template de PR review os comentários de “função grande demais”, “nome não descritivo”, “extraia pra variável”. Troque por: “essa mudança é fácil de desfazer se precisar?” Se a resposta for sim, aprova. A reversibilidade é a única qualidade que importa em código novo.
- Recompense lead time, não score. Reconhecimento público do time deve ir pra quem entregou feature, não pra quem subiu nota de análise estática. Score de ferramenta é diagnóstico, não desempenho. A diferença parece sutil, mas muda o que o time prioriza.
- Faça o ônus da refatoração ser explícito. Antes de aprovar um PR que refatora, pergunte: “qual métrica de entrega isso vai mover?” Se a resposta for “vai diminuir complexidade ciclomática”, recuse. Complexidade ciclomática não é métrica de entrega. Se a resposta for “vai eliminar a categoria de bug que apareceu 3 vezes esse trimestre”, aprove e meça o resultado por 3 meses.
Não é “pare de refatorar”. É “refatore por motivo, não por checklist”. A diferença cabe em uma frase, mas muda a cultura inteira do time.
Conclusão
Clean Code é referência canônica. Não é lei. O capítulo 3 tem ressalvas que a cultura apagou, e o custo de aplicar o checklist sem as ressalvas é código bem organizado que ninguém consegue entregar.
A pergunta certa não é “este código está limpo o suficiente?”. É “este código está resolvendo o problema com menos dor de cabeça?”. E a segunda pergunta não tem checklist que responda.
Como você mede dor de cabeça no seu time?







Um comentário