Code Smell Explicado: De Buzzword a Guia Prático de Refatoração
O que code smells realmente são, como identificar, e como as ferramentas evoluíram do catálogo do Fowler até detecção por IA. Uma perspectiva then-vs-now de uma pergunta real no SO.
Code Smell Explicado: De Buzzword a Guia Prático de Refatoração
Em 2015, enquanto aprendia Swift e tentava escrever “código limpo,” fiz uma pergunta no Stack Overflow em Português sobre Code Smell. Eu tinha visto o termo em todo lugar — blog posts, palestras, site do Martin Fowler — mas não conseguia definir o que significava na prática. Era a mesma coisa que violar o DRY? Era só outro nome pra “código ruim”?
A pergunta recebeu 26 upvotes, o que me diz que eu não era o único confuso. Onze anos depois, tenho uma visão bem mais clara — e ferramentas melhores pra lidar com isso.
O Entendimento de 2015: Pensamento Baseado em Catálogo
Naquela época, meu entendimento sobre code smells era puramente acadêmico. Tinha lido o livro Refactoring do Fowler (bom, partes dele), passeado pela Wikipedia, e decorado uma lista de nomes: Long Method, God Class, Feature Envy, Shotgun Surgery. Parecia coleção de figurinha — eu sabia os nomes, mas nem sempre reconhecia na prática.
A maior confusão era misturar code smells com princípios de design. Minha pergunta no SO perguntava especificamente se code smell era “a mesma coisa” que violar DRY, KISS ou YAGNI. A resposta que recebi (e eventualmente entendi) é não — mas a relação é mais sutil do que um simples “são coisas diferentes.”
Eu tratava code smells como regras: “se um método tem mais de 20 linhas, tem cheiro ruim.” Essa abordagem mecânica perdia completamente o ponto.
Code Smells Comuns (Com Código de Verdade)
Deixa eu mostrar o que eu gostaria que alguém tivesse me mostrado em 2015 — exemplos concretos ao invés de definições abstratas.
Long Method (Método Longo)
// Essa função faz autenticação, validação, logging,
// inserção no banco e envio de email. Tudo no mesmo lugar.
async function registerUser(req, res) {
const { email, password, name } = req.body;
if (!email || !email.includes('@')) return res.status(400).send('Email inválido');
if (!password || password.length < 8) return res.status(400).send('Senha fraca');
if (!name || name.length < 2) return res.status(400).send('Nome muito curto');
const existing = await db.users.findOne({ email });
if (existing) return res.status(409).send('Email já cadastrado');
const hashed = await bcrypt.hash(password, 12);
const user = await db.users.insertOne({ email, password: hashed, name, createdAt: new Date() });
await sendEmail(email, 'Bem-vindo!', `Oi ${name}, obrigado por se cadastrar.`);
logger.info(`Novo usuário registrado: ${email}`);
res.status(201).json({ id: user.insertedId });
}
Funciona. Passa nos testes. Mas toda vez que você precisa mudar qualquer coisa — regras de validação, template de email, formato de log — está editando essa mesma função. O smell aqui não é a quantidade de linhas, é o número de razões que essa função tem pra mudar.
God Class (Classe Deus)
class UserManager {
createUser(data) {
/* ... */
}
deleteUser(id) {
/* ... */
}
sendWelcomeEmail(user) {
/* ... */
}
generateInvoice(user) {
/* ... */
}
calculateDiscount(user) {
/* ... */
}
exportToCSV(users) {
/* ... */
}
syncWithCRM(user) {
/* ... */
}
validateAddress(user) {
/* ... */
}
}
Quando o nome de uma classe termina com “Manager,” “Helper” ou “Utils” e tem 15+ métodos tocando domínios não relacionados, você tem uma God Class. O UserManager aqui lida com email, faturamento, exportação, CRM e validação de endereço — cada um desses merece seu próprio módulo.
Feature Envy (Inveja de Feature)
function calculateShippingCost(order) {
const weight = order.items.reduce((sum, item) => sum + item.weight, 0);
const distance = getDistance(order.warehouse.zip, order.customer.address.zip);
const rate = order.customer.isPrime ? 0.5 : 1.0;
return weight * distance * rate;
}
Essa função mergulha fundo no objeto order — seus items, warehouse, customer, e o address do customer. Ela sabe mais sobre os internos de Order do que sobre o próprio módulo. Essa lógica provavelmente pertence dentro da classe Order (ou de um ShippingCalculator dedicado que recebe dados já extraídos).
Código Duplicado (O Tipo Sutil)
// Em userController.js
const user = await db.findById(id);
if (!user) return res.status(404).json({ error: 'Não encontrado' });
// Em orderController.js
const order = await db.orders.findById(id);
if (!order) return res.status(404).json({ error: 'Não encontrado' });
// Em productController.js
const product = await db.products.findById(id);
if (!product) return res.status(404).json({ error: 'Não encontrado' });
Não é copy-paste (os nomes das collections mudam), mas o padrão é idêntico. Um middleware ou helper simples eliminaria a repetição e centralizaria o comportamento de 404.
A Abordagem de 2026: Smells São Detectados Automaticamente
Eis o que mudou mais drasticamente desde 2015: as ferramentas alcançaram o conceito.
ESLint e SonarQube agora têm regras que flagram code smells por padrão. max-lines-per-function, max-params, complexity (threshold de complexidade ciclomática) — não são preferências de estilo, são detectores de smell. O SonarQube inclusive categoriza issues como “Code Smell” explicitamente e estima a dívida técnica em minutos.
Code review com IA é a verdadeira virada de jogo. GitHub Copilot code review, Claude na IDE, e ferramentas como CodeRabbit pegam padrões que linters baseados em regras não conseguem. Eles conseguem identificar Feature Envy, sugerir extração de classe, ou sinalizar que o nome de uma função não bate com o que ela realmente faz. Já tive o Claude pegando um padrão de God Class que tinha se acumulado ao longo de 18 meses de commits “só mais um método” — algo que nenhuma regra de linter flagraria porque nenhum commit isolado era o problema.
Integrações na IDE fecham o ciclo. O sistema de inspeção do IntelliJ, extensões do VS Code como SonarLint — eles mostram smells em tempo real enquanto você digita, com sugestões de refatoração em um clique. O ciclo de feedback que costumava levar dias (code review) ou semanas (auditoria de dívida técnica) agora leva segundos.
DRY/KISS/YAGNI vs Code Smell: Relacionados, mas Diferentes
Essa era a essência da minha confusão de 2015, então vou ser explícito.
DRY, KISS e YAGNI são princípios — eles dizem o que mirar. Não se repita. Mantenha simples. Não construa o que você não precisa ainda.
Code smells são sintomas — eles dizem que algo pode estar errado. Um método longo é um sintoma. Código duplicado é um sintoma. Uma classe com 30 campos é um sintoma.
A relação: violar o DRY frequentemente produz o smell de Código Duplicado. Violar o KISS frequentemente produz Long Method ou abstrações excessivamente complexas. Violar o YAGNI produz Speculative Generality (construir abstrações pra casos de uso que nunca chegam).
Mas um code smell nem sempre significa que um princípio foi violado. Às vezes um método de 50 linhas é a forma mais clara de expressar um algoritmo complexo. Às vezes duplicação é melhor que a abstração errada. O smell é um sinal pra investigar, não um veredito automático.
Conclusão
Se eu pudesse voltar e editar minha pergunta de 2015 no SO, reformularia completamente. Eu estava procurando uma definição quando deveria estar procurando uma mentalidade.
Code smells são sintomas, não doenças. São seu codebase sussurrando “ei, olha isso aqui.” A cura nunca é mecânica — não é “método > 20 linhas, logo divide.” A cura é entender por que o código está ali, o que ele está tentando fazer, e se uma estrutura diferente tornaria mais fácil de mudar.
As ferramentas que temos em 2026 — linters, revisores com IA, inspeções na IDE — são incríveis pra encontrar smells. Mas decidir o que fazer com eles? Isso ainda precisa de um desenvolvedor que entende o contexto. E esse entendimento, mais do que qualquer catálogo ou acrônimo, é o que torna a refatoração eficaz.
Posts Relacionados
Code Smell Explicado: De Buzzword a Guia Prático de Refatoração
O que code smells realmente são, como identificar, e como as ferramentas evoluíram do catálogo do Fowler até detecção por IA. Uma perspectiva then-vs-now de uma pergunta real no SO.
PHP Namespaces e Autoload: Do PSR-0 ao PHP Moderno
De uma resposta de 2015 no Stack Overflow explicando namespaces e autoloading em PHP ao mundo PSR-4, Composer e PHP 8.x de 2026.
Reescrever vs Melhorar Incrementalmente: A Pergunta Que Todo Time Enfrenta
De uma resposta de 2015 no Stack Overflow sobre reescrever software de faturamento até a realidade do strangler fig em 2026 — quando reescrever, quando melhorar, e por que a resposta geralmente é os dois.