💡 Key Takeaways
- Rule I Follow #1: Functions Should Do One Thing (But I Define "One Thing" Differently)
- Rule I Follow #2: Meaningful Names Are Non-Negotiable
- Rule I Follow #3: Comments Explain Why, Not What
- Rule I Follow #4: Keep Functions and Classes Small (With Nuance)
Eu estive analisando o código de outras pessoas por 14 anos agora como arquiteto de software sênior em uma empresa fintech de médio porte, e posso te dizer exatamente quando parei de ser um zelador do código limpo: foi às 2:47 AM de uma terça-feira em março de 2019, quando nosso sistema de processamento de pagamentos caiu porque alguém passou três dias refatorando um módulo perfeitamente funcional para seguir cada uma das regras do livro do tio Bob. A ironia? O bug foi introduzido durante a "limpeza".
💡 Principais Conclusões
- Regra que eu sigo #1: Funções devem fazer uma coisa (mas eu defino "uma coisa" de maneira diferente)
- Regra que eu sigo #2: Nomes significativos são inegociáveis
- Regra que eu sigo #3: Comentários explicam o porquê, não o quê
- Regra que eu sigo #4: Mantenha funções e classes pequenas (com nuance)
Essa noite mudou a forma como penso sobre a qualidade do código. Não estou dizendo que os princípios do código limpo estão errados—longe disso. Mas após revisar mais de 10.000 solicitações de pull, orientar 47 desenvolvedores e entregar 23 grandes lançamentos de produtos, aprendi que a adesão dogmática a qualquer conjunto de regras é apenas mais uma forma de dívida técnica. Algumas regras de código limpo são ouro absoluto. Outras? Elas são, na melhor das hipóteses, dependentes do contexto e, na pior, ativamente prejudiciais.
Aqui está o que eu realmente faço em código de produção e, mais importante, por que eu faço isso.
Regra que eu sigo #1: Funções devem fazer uma coisa (mas eu defino "uma coisa" de maneira diferente)
O princípio da responsabilidade única para funções é provavelmente a regra mais valiosa que sigo religiosamente. Mas aqui está onde eu me desvio do livro didático: eu não meço "uma coisa" por linhas de código ou pelo número de operações. Eu meço por coesão conceitual.
No último trimestre, revisei uma função que tinha 8 linhas, mas violava o SRP espetacularmente. Ela validou a entrada do usuário E registrou o resultado da validação E atualizou um cache. Três responsabilidades distintas espremidas em 8 linhas. Compare isso com uma função de 45 linhas que escrevi no mês passado que orquestra uma transação complexa no banco de dados—ela faz "uma coisa" (completar uma transação de pagamento), mas essa única coisa requer múltiplas etapas que pertencem juntas.
Aqui está meu teste de avaliação: Posso descrever o que essa função faz em uma única frase sem usar a palavra "e"? Se eu precisar dizer "essa função valida a entrada E envia um e-mail," ela está fazendo duas coisas. Mas se eu digo "essa função processa um pedido de reembolso," e isso naturalmente envolve validação, atualizações de banco de dados e notificação—isso ainda é uma coisa no nível certo de abstração.
Na prática, isso significa que minhas funções têm em média 25-30 linhas em vez das 10-15 que os puristas recomendam. Mas nossa taxa de bugs nessas funções é 40% menor do que no código excessivamente extraído que tínhamos antes. Por quê? Porque manter operações relacionadas juntas reduz a carga cognitiva de entender o sistema. Quando tudo está dividido em funções pequenas, você passa mais tempo saltando entre arquivos do que entendendo a lógica de negócios.
A verdadeira vitória aqui é a testabilidade. Uma função que faz uma única coisa conceitual é fácil de testar, mesmo que tenha 40 linhas. Você simula as dependências, chama a função, afirma o resultado. Pronto. Quando você extrai tudo em funções de 5 linhas, você acaba com testes de integração de qualquer maneira porque o teste unitário se torna sem sentido.
Regra que eu sigo #2: Nomes significativos são inegociáveis
Eu vou morrer nessa colina: nomes de variáveis e funções são a documentação mais importante que você jamais escreverá. Eu rejeitei solicitações de pull apenas por causa de nomes ruins, e farei isso novamente.
"A adesão dogmática a qualquer conjunto de regras é apenas mais uma forma de dívida técnica. O melhor código não é o mais limpo—é o código que é enviado de forma confiável e pode ser mantido pela sua equipe."
Dois meses atrás, um desenvolvedor júnior submeteu código com uma função chamada `processData()`. Eu enviei de volta com um vídeo de 10 minutos explicando o porquê. Essa função estava validando especificamente números de cartões de pagamento contra o algoritmo de Luhn. O nome correto era `validateCardNumberChecksum()`. Sim, é mais longo. Sim, é mais específico. Esse é exatamente o ponto.
Aqui está minha hierarquia de nomes, refinada ao longo de milhares de revisões de código:
- Variáveis booleanas: Sempre comece com is/has/can/should. Não `active`, mas `isActive`. Não `permission`, mas `hasPermission`.
- Funções: Verbos para ações, substantivos para consultas. `calculateTotalPrice()` não `totalPrice()`. `getUserById()` não `user()`.
- Classes: Substantivos que representam conceitos, não ações. `PaymentProcessor` não `ProcessPayments`.
- Constantes: SCREAMING_SNAKE_CASE para constantes verdadeiras, camelCase para configurações que podem mudar.
O impacto é mensurável. Após implementar convenções rígidas de nomenclatura em nossa equipe há 18 meses, nosso tempo médio de revisão de PR caiu de 3.2 horas para 1.8 horas. Por quê? Porque os revisores gastam menos tempo decifrando o que o código faz e mais tempo avaliando se ele faz isso corretamente.
Eu também aplico uma regra de "sem abreviações" com exatamente três exceções: `id`, `url`, e `api`. Tudo o mais é escrito por extenso. `usr` se torna `user`. `btn` se torna `button`. `calc` se torna `calculate`. As teclas extras valem a pena quando alguém está depurando às 11 PM e não precisa adivinhar o que `tmpBfr` significa.
Regra que eu sigo #3: Comentários explicam o porquê, não o quê
Eu vi dois extremos na minha carreira: bases de código sem comentários e bases de código onde cada linha tem um comentário. Ambos estão errados, mas o código excessivamente comentado é, na verdade, pior porque cria uma carga de manutenção e muitas vezes mente.
| Regra de Código Limpo | Quando Seguir | Quando Ignorar | Impacto no Mundo Real |
|---|---|---|---|
| Funções devem ser pequenas | Caminhos de código de alto tráfego, módulos frequentemente modificados | Lógica de orquestração complexa, manuseio de transações | Divisões prematuras criam sobrecarga de navegação |
| Sem comentários no código | Lógica de negócios autoexplicativa | Algoritmos complexos, requisitos regulatórios, otimizações não óbvias | Falta de contexto custa horas em depuração |
| DRY (Não Repita) | Lógica de negócios central, transformações de dados | Código semelhante, mas contextualmente diferente | Excesso de abstração cria dependências frágeis |
| Evitar obsessão primitiva | Modelos de domínio, limites de API | Utilitários internos simples, caminhos críticos de desempenho | Encapsulamento excessivo adiciona carga cognitiva |
Minha regra é simples: se você está explicando o que o código faz, o código provavelmente é ruim. Se você está explicando por que tomou uma decisão específica, esse é um bom comentário. Aqui está um exemplo real do que aconteceu...