Engenharia de Software: Princípios de Código Limpo
Durante a carreira profissional, muitas vezes trabalhamos em algoritmos complexos ou arquiteturas distribuídas; e, no meio da correria do dia-a-dia, esquecemos que o código é lido muito mais vezes do que é escrito. Com isto em mente, Robert C. Martin – conhecido como “Uncle Bob” – elaborou uma série de sugestões para elaboração de código com o intuito de melhorar a legibilidade. Essa abordagem ficou conhecida como Código Limpo (do inglês, Clean Code).
O que é clean code?
Clean code é uma abordagem de escrita de software voltada para legibilidade, simplicidade, manutenção e clareza de intenção. Em termos práticos, significa produzir código que outras pessoas — e o próprio autor, no futuro — consigam ler, entender, corrigir e evoluir com menor esforço. Robert C. Martin afirma que código limpo deve ser fácil de ler e de modificar, reduzindo o custo de manutenção e o acúmulo de complexidade técnica (Martin, 2009).
Apesar das ideias serem comumente atribuídas apenas ao Uncle bob – afinal, o livro dele chama-se Clean Code -, é importante destacarmos que elas não nasceram de um único autor. Ela é resultado de uma tradição de engenharia de software que valoriza clareza, abstração, coesão, baixo acoplamento e boa comunicação por meio do código. Entre os autores mais influentes estão Robert C. Martin (obviamente), Martin Fowler, Kent Beck, Steve McConnell, Andrew Hunt e David Thomas. Cada um enfatiza aspectos distintos, mas convergentes: código é um ativo intelectual e sua qualidade interna importa tanto quanto seu funcionamento externo (Fowler, 2018; McConnell, 2004; Hunt; Thomas, 1999).
Propriedades fundamentais do clean code
Um código limpo costuma apresentar algumas propriedades recorrentes. A primeira é a legibilidade. O leitor deve conseguir entender rapidamente o que o código faz e por quê. A segunda é a simplicidade. Soluções desnecessariamente sofisticadas elevam o custo cognitivo. A terceira é a coesão: cada módulo, função ou classe deve concentrar uma responsabilidade clara. A quarta é o baixo acoplamento, isto é, a redução de dependências rígidas entre partes do sistema. A quinta é a capacidade de evolução: código limpo facilita testes, refatorações e extensão futura (Martin, 2009; Fowler, 2018).
Em JavaScript, esses princípios são particularmente importantes porque a linguagem é flexível e permissiva. Essa flexibilidade é poderosa, mas também facilita a produção de código inconsistente, ambíguo ou excessivamente implícito.
Nomes significativos
Um dos princípios mais conhecidos de Robert C. Martin é que nomes devem revelar intenção. De acordo com Martin (2009), um nome de variável ideal deve possuir três características essenciais:
- Revelar a intenção: o nome deve responder por que a variável existe, o que ela faz e como é usada.
- Evitar desinformação: nomes não devem sugerir um tipo ou propósito diferente do real.
- Ser pronunciável e buscável: se você não consegue pronunciar o nome, não consegue discutir o código. Além disso, se o nome é curto demais (como
e), ele se torna impossível de localizar em uma base de código extensa.
A partir das características, percebemos que nomes das variáveis, funções e classes não devem exigir interpretação excessiva do desenvolvedor. Logo, um bom nome reduz a necessidade de comentários explicativos (Martin, 2009).
Técnicas e princípios práticos
- Use nomes que revelam intenção: evite nomes genéricos ou codificados. Se um nome requer um comentário para explicar seu significado, então o nome falhou em sua missão original.
- Evite codificações e notação húngara: antigamente, era comum prefixar variáveis com seu tipo (ex:
strName,iCount). Em linguagens modernas e com IDEs potentes, essa prática é considerada ruído visual. Conforme Martin (2009) descreve, nomes de variáveis devem focar no domínio do problema, não na implementação técnica. - Use nomes de classes com substantivos e métodos com verbos: as classes devem nomeadas com substantivos ou frases substantivadas (ex:
Customer,WikiPage,Account), evitando-se nomes genéricos comoDataouInfo. Enquanto isso, os métodos devem conter verbos ou frases verbais (ex:postPayment,deletePage,save). - Selecione uma palavra por conceito: mantenha a consistência léxica. Se você usa
fetchpara buscar dados em um serviço, não useretrieveougetem outro serviço para a mesma finalidade. Escolha um termo e atenha-se a ele em todo o projeto.
Agora, vejamos alguns exemplos de códigos ruins e bons, segundo a filosofia do clean code. Apenas, uma rápida observação: apesar dos devs brasileiros falarem a língua portuguesa como via de regra, acreditamos que seja uma boa prática apresentar os códigos em inglês, pois consideramos que treinar a programação na língua inglesa pode ajudar nas buscas por empregos fora do país (o famoso, “trampo na gringa”).
O Código 1 é bem simples e funcionará num ambiente de javascript. Contudo, ele não comunica claramente o significado das variáveis a, b, c e r.
function p(a, b, c) {
if (c) {
return a * b;
}
return a + b;
}
const r = p(50, 3, true);
console.log(r);Refatorando o Código 1 para melhorar a legibilidade, obtemos o Código 2.
function calcularValorItem(preco, quantidade, deveMultiplicar) {
if (deveMultiplicar) {
return preco * quantidade;
}
return preco + quantidade;
}
const totalValorItem = calcularValorItem(50, 3, true);
console.log(totalValorItem);A refatoração acima ainda não é ideal, mas já melhora a semântica inicial. Vamos melhorar isso um pouco mais, gerando o Código 3.
function calcularSubtotalItemCarrinho(precoUnitario, quantidade) {
return precoUnitario * quantidade;
}
function calcularValoresCombinados(primeiroValor, segundoValor) {
return primeiroValor + segundoValor;
}
const carrinhoSubtotal = calcularSubtotalItemCarrinho(50, 3);
console.log(carrinhoSubtotal);Aqui o problema foi melhor resolvido: em vez de uma função genérica controlada por uma flag, criam-se funções com propósito explícito. Fowler (2018) observa que remover generalizações desnecessárias costuma melhorar o design e facilitar a manutenção.
Seguir esses passos simples nos oferecem algumas vantagens tais como: manutenibilidade, reduzindo drasticamente o tempo que um desenvolvedor leva para entender um sistema legado; redução de bugs, pois nomes claros expõem inconsistências lógicas de forma mais evidente; e documentação viva, tornando o código autoexplicativo, reduzindo a necessidade de documentação externa que frequentemente fica desatualizada.
Em contrapartida, o tempo Inicial de desenvolvimento tende a aumentar, pois escrever código limpo exige mais reflexão inicial, o que pode parecer “lento” em ambientes de extrema pressão por entrega. No entanto, o custo de manutenção de um código “rápido e sujo” é exponencialmente maior a longo prazo. Além disso, nomes descritivos tendem a ser mais longos. Então, o desafio do engenheiro de software é encontrar o equilíbrio entre verbosidade e concisão, ou seja, o ponto onde o nome é longo o suficiente para ser claro, mas não tão extenso que torne a linha de código ilegível.
Funções pequenas e com responsabilidade única
Uncle Bob defende que funções devem fazer uma única coisa, fazê-la bem e fazê-la apenas. Quando uma função mistura validação, cálculo, persistência e exibição, ela se torna mais difícil de entender, testar e modificar (Martin, 2009).
A nomenclatura de funções também é algo muito importante, pois ela é a primeira camada de documentação do seu sistema. Uma função bem nomeada deve ser reveladora de intenção (assim como as variáveis). Se você precisa ler o corpo da função para entender o que ela faz, o nome da função falhou em legibilidade.
Segundo os princípios estabelecidos no clean code, um nome ideal possui as seguintes propriedades:
- Verbos e frases verbais: funções representam ações; logo, devem conter verbos.
- Abstração coerente: o nome deve refletir o nível de abstração em que a função opera.
- Evitar desinformação: nomes não devem sugerir algo que a função não entrega.
- Pronunciabilidade: se você não consegue discutir o nome em uma reunião de design sem parecer que está tendo um espasmo, o nome está errado.
Analisemos o Código 4 para entender porque ele é considerado um código ruim. Observe que a função processarOrdem valida, calcula, imprime, salva e envia e-mail para o consumidor. Ela é responsável por várias responsabilidades.
function processarOrdem(ordem) {
if (!ordem.items || ordem.items.length === 0) {
console.log("Pedido inválido");
return;
}
let total = 0;
for (const item of ordem.items) {
total += item.preco * item.quantidade;
}
console.log(`Total: R$ ${total}`);
salvarOrdemNoBancoDeDados(ordem, total);
enviarEmailConfirmacao(ordem.emailConsumidor, total);
}Refatorando o código original, obtemos uma separação das responsabilidades em várias funções, de forma que uma função chame outra para realizar aquela parte do processo e as responsabilidades sejam únicas.
function OrdemValida(ordem) {
return Array.isArray(ordem.items) && ordem.items.length > 0;
}
function calcularTotalOrdem(items) {
return items.reduce((total, item) => total + item.preco * item.quantidade, 0);
}
function processarOrdem(ordem) {
if (!OrdemValida(ordem)) {
console.log("Pedido inválido");
return;
}
const total = calcularTotalOrdem(ordem.items);
console.log(`Total: R$ ${total}`);
salvarOrdemNoBancoDeDados(ordem, total);
enviarEmailConfirmacao(ordem.emailConsumidor, total);
}O Código 5 ainda pode ser melhorado, mas a lógica principal ficou bem mais clara que a versão original. McConnell (2004) argumenta que decompor rotinas em partes menores reduz complexidade mental e facilita revisão e testes.
Conclusão
O design de código com clean code não deve ser considerado uma receita rígida e infalível, mas sim uma lista de propostas para engenharia de sistemas. Apesar de diferenças aqui e ali, os autores clássicos convergem na ideia de que código deve comunicar intenção, minimizar complexidade, evitar duplicação e facilitar mudanças futuras.
O ponto central ao desenvolver-se sistemas complexos não é apenas “funcionar”, mas funcionar de uma forma que preserve a capacidade de compreensão e evolução do software. Em termos profissionais, escrever código limpo é assumir que manutenção não é acidente; é parte central do desenvolvimento.
Obrigado pela leitura e bons estudos!
Referências
BECK, Kent. Test-driven development: by example. Boston: Addison-Wesley, 2003.
FOWLER, Martin. Refactoring: improving the design of existing code. 2. ed. Boston: Addison-Wesley, 2018.
HUNT, Andrew; THOMAS, David. The pragmatic programmer: from journeyman to master. Boston: Addison-Wesley, 1999.
MARTIN, Robert C. Clean code: a handbook of agile software craftsmanship. Boston: Prentice Hall, 2009.
MCCONNELL, Steve. Code complete. 2. ed. Redmond: Microsoft Press, 2004.


