Clean code em JavaScript
LidoEsta aula ensina os princípios fundamentais de clean code em JavaScript, abordando desde a escolha de nomes claros e expressivos até a criação de funções pequenas e a redução de efeitos colaterais. Você aprenderá a escrever código mais legível, sustentável e fácil de manter, com exemplos práticos e exercícios para fixar o conteúdo.
Clean code é um conjunto de práticas e princípios que visam tornar o código mais legível, simples e fácil de manter. No contexto de JavaScript, escrever código limpo é essencial para projetos de qualquer tamanho, pois facilita a colaboração, reduz a complexidade e minimiza a introdução de bugs. Nesta aula, vamos explorar os pilares do clean code, com foco em nomes, funções pequenas, efeitos colaterais e comentários, sempre com exemplos práticos em JavaScript.
O objetivo é que você desenvolva um olhar crítico sobre o seu próprio código e adote hábitos que melhorem a qualidade do que você produz. Lembre-se: código limpo não é apenas sobre funcionar, mas sobre ser compreensível para outros desenvolvedores (e para o seu "eu" do futuro).
Nomes
Nomes são a forma mais direta de comunicação no código. Um bom nome deve revelar a intenção, ser pronunciável, buscável e evitar abreviações desnecessárias. Em JavaScript, isso se aplica a variáveis, funções, classes e propriedades.
Considere o exemplo abaixo: o que significa d? E calc? Em contraste, nomes como daysSinceLastLogin ou calculateTotalPrice são autoexplicativos. Além disso, evite nomes genéricos como data, list, obj, que não comunicam nada sobre o conteúdo. Prefira nomes específicos que descrevam o papel daquela entidade no domínio do problema.
// Ruim
const d = new Date();
const calc = (a, b) => a * b;
// Bom
const currentDate = new Date();
const calculateArea = (width, height) => width * height;Outra prática importante é usar nomes booleanos que façam sentido em uma pergunta, como isActive, hasPermission, canEdit. Isso torna as condições muito mais legíveis. Para funções, use verbos que indiquem ação: getUser, sendEmail, validateInput. Evite variações desnecessárias como getUser e fetchUser para a mesma operação; escolha um padrão e mantenha consistência.
// Ruim
let flag = true;
function process() { /* ... */ }
// Bom
let isUserLoggedIn = true;
function processPayment() { /* ... */ }Funções pequenas
Funções pequenas são mais fáceis de entender, testar e reutilizar. A regra prática é que uma função deve fazer apenas uma coisa, e fazer bem. Se você precisa de comentários para explicar o que a função faz, ou se ela tem muitos parâmetros e níveis de indentação, provavelmente está grande demais.
Uma função ideal tem entre 2 e 10 linhas, um nome claro que descreve o que ela faz, e poucos parâmetros (idealmente 0, 1 ou 2). Se muitos parâmetros são necessários, considere agrupá-los em um objeto. Além disso, funções devem ter responsabilidade única: se você está fazendo validação, formatação e envio em uma única função, divida em funções menores.
// Ruim: função faz muitas coisas
function processOrder(order) {
// valida
if (!order.items.length) throw new Error('Empty order');
// calcula total
let total = 0;
for (let item of order.items) total += item.price * item.quantity;
// aplica desconto
if (order.coupon) total *= 0.9;
// salva no banco
db.save(order, total);
// envia email
email.send(order.user, total);
return total;
}
// Bom: funções pequenas e focadas
function validateOrder(order) { /* ... */ }
function calculateTotal(order) { /* ... */ }
function applyDiscount(total, coupon) { /* ... */ }
function saveOrder(order, total) { /* ... */ }
function sendConfirmationEmail(user, total) { /* ... */ }Além de reduzir o tamanho, funções pequenas facilitam a testabilidade: você pode testar cada parte isoladamente. Elas também promovem a reutilização, pois comportamentos específicos podem ser combinados de diferentes maneiras. Em JavaScript, funções curtas combinam bem com arrow functions e destructuring, tornando o código mais expressivo.
Evitando efeitos colaterais
Efeitos colaterais são modificações no estado do sistema que vão além do retorno da função. Exemplos incluem alterar variáveis globais, modificar argumentos, escrever em arquivos, fazer requisições HTTP ou manipular o DOM. Em clean code, buscamos minimizar efeitos colaterais, pois eles tornam o comportamento imprevisível e difícil de testar.
Uma função ideal é pura: dado o mesmo input, sempre retorna o mesmo output, sem modificar nada externo. Isso não significa que você nunca deve ter efeitos colaterais — em aplicações reais, você precisa deles (por exemplo, salvar dados no banco). Mas você deve isolá-los em funções específicas, longe da lógica de negócio, e deixar claro no nome que há efeito colateral (como saveToDatabase).
// Ruim: função modifica o array original (efeito colateral)
function addItemToCart(cart, item) {
cart.push(item);
return cart;
}
// Bom: função retorna um novo array, sem modificar o original
function addItemToCart(cart, item) {
return [...cart, item];
}Outro ponto é evitar modificar objetos e arrays passados como parâmetros. Use imutabilidade sempre que possível, ou crie cópias antes de alterar. No JavaScript moderno, você pode usar o spread operator, Object.assign ou métodos como map e filter que retornam novos arrays. Isso evita bugs sutis e facilita o rastreamento de mudanças de estado.
// Ruim: mutação de objeto
function updateUser(user, newName) {
user.name = newName;
return user;
}
// Bom: retorna novo objeto
function updateUser(user, newName) {
return { ...user, name: newName };
}Comentários úteis
Comentários devem explicar o porquê, não o o quê. O código já diz o que está acontecendo; comentários que repetem o código são ruído. Por exemplo, // incrementa i ao lado de i++ é inútil. Em vez disso, use comentários para explicar decisões de design, contextos de negócio, ou alertar sobre armadilhas.
Bons comentários incluem: explicação de uma lógica complexa, contexto histórico (por que essa abordagem foi escolhida), referências a issues ou documentação, e avisos sobre pré-condições. Comentários também são úteis para documentar APIs públicas, mas prefira JSDoc para isso, pois pode ser extraído automaticamente.
// Ruim: comentário desnecessário
const total = price * quantity; // multiplica preço pela quantidade
// Bom: explica o porquê
// Usamos desconto de 10% para clientes VIP (regra de negócio)
const total = price * quantity * 0.9;Outra prática é evitar comentários que se tornam facilmente desatualizados. Se o código mudar, o comentário pode ficar incorreto e enganar outros desenvolvedores. Em vez de comentar, prefira extrair a lógica para uma função com nome claro. Se um comentário é necessário, mantenha-o curto e atualizado.
// Ruim: comentário que se torna falso
function getUsers() {
// retorna usuários ativos
return db.query('SELECT * FROM users'); // na verdade, retorna todos
}
// Bom: nome de função claro + comentário de porquê
function getActiveUsers() {
// Precisamos apenas de usuários ativos por causa da política de privacidade
return db.query('SELECT * FROM users WHERE active = 1');
}Boas práticas e observações finais
Além dos tópicos acima, lembre-se de que clean code é um processo contínuo. Revise seu código regularmente, busque feedback e refatore quando necessário. Ferramentas como ESLint podem ajudar a manter padrões consistentes, e testes automatizados garantem que suas refatorações não quebrem nada.
Outra dica: siga o princípio DRY (Don't Repeat Yourself) para evitar duplicação, mas não exagere na abstração. Código limpo é equilibrado: simples o suficiente para ser compreendido, mas não simplificado ao ponto de esconder a complexidade necessária.
Exercícios
- Refatore o seguinte código para melhorar os nomes de variáveis e funções:
const x = 5; const y = 3; function f(a, b) { return a * b + 10; } console.log(f(x, y)); - A função abaixo faz várias coisas. Divida-a em funções menores e com nomes claros:
function processOrder(order) { let total = 0; for (let item of order.items) { total += item.price * item.quantity; } if (order.coupon) total *= 0.9; console.log('Total:', total); sendEmail(order.user, total); } - Reescreva a função a seguir para evitar efeitos colaterais, retornando um novo array:
function addItem(cart, item) { cart.push(item); return cart; } - Escreva um comentário útil para uma função que calcula a idade a partir da data de nascimento, explicando por que ela usa ano bissexto de forma específica.
- Identifique os problemas de nomenclatura e efeitos colaterais no código abaixo e refatore:
let count = 0; function inc() { count++; } function show() { console.log(count); } inc(); inc(); show();
const basePrice = 5;
const quantity = 3;
const TAX_AMOUNT = 10;
function calculateTotalWithTax(price, qty) {
return price * qty + TAX_AMOUNT;
}
console.log(calculateTotalWithTax(basePrice, quantity));function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
function applyCoupon(total, coupon) {
return coupon ? total * 0.9 : total;
}
function processOrder(order) {
const total = applyCoupon(calculateTotal(order.items), order.coupon);
console.log('Total:', total);
sendEmail(order.user, total);
}function addItem(cart, item) {
return [...cart, item];
}// Calcula a idade em anos. Usamos a data de nascimento e a data atual.
// Consideramos ano bissexto para garantir precisão no cálculo de dias.
function calculateAge(birthDate) {
// ...
}let count = 0;
function incrementCounter() {
count++;
}
function logCounter() {
console.log(count);
}
incrementCounter();
incrementCounter();
logCounter();Ou, melhor ainda, evite o estado global usando uma função pura:function createCounter() {
let count = 0;
return {
increment: () => ++count,
log: () => console.log(count)
};
}
const counter = createCounter();
counter.increment();
counter.increment();
counter.log();