Princípios YAGNI e DRY: quando aplicar
1. Introdução aos princípios YAGNI e DRY
No desenvolvimento de software, dois princípios fundamentais frequentemente entram em conflito: YAGNI (You Aren't Gonna Need It) e DRY (Don't Repeat Yourself). O YAGNI, originado no Extreme Programming (XP) por Kent Beck, prega que você não deve adicionar funcionalidades até que sejam realmente necessárias. Já o DRY, cunhado por Andy Hunt e Dave Thomas em "The Pragmatic Programmer", defende que cada conhecimento deve ter uma representação única, inequívoca e autoritária dentro de um sistema.
Compreender quando aplicar cada princípio é essencial para evitar tanto o excesso de abstração quanto a duplicação desnecessária de código. Este artigo explora o equilíbrio entre YAGNI e DRY no contexto de Temas — Lista Final (1200 temas), oferecendo exemplos práticos e diretrizes para decisões conscientes.
2. YAGNI: evitando funcionalidades desnecessárias
YAGNI é particularmente valioso em projetos com requisitos voláteis e ciclos de desenvolvimento curtos. A armadilha mais comum é a superengenharia: criar abstrações complexas "por precaução", antecipando necessidades futuras que podem nunca se materializar.
Exemplo de violação de YAGNI:
# Código que antecipa múltiplos tipos de desconto em um sistema simples
class CalculadoraDesconto:
def __init__(self):
self.estrategias = {
'percentual': self.desconto_percentual,
'fixo': self.desconto_fixo,
'cumulativo': self.desconto_cumulativo,
'sazonal': self.desconto_sazonal # Ainda não requisitado
}
def calcular(self, tipo, valor, parametro):
return self.estrategias.get(tipo, lambda: 0)(valor, parametro)
def desconto_percentual(self, valor, percentual):
return valor * (1 - percentual/100)
def desconto_fixo(self, valor, fixo):
return valor - fixo
def desconto_cumulativo(self, valor, niveis):
# Complexidade desnecessária se apenas descontos simples são requisitados
pass
def desconto_sazonal(self, valor, temporada):
# Funcionalidade especulativa
pass
Estratégias para aplicar YAGNI:
- Implemente apenas o que o requisito atual exige
- Resista à tentação de criar "hooks" genéricos para funcionalidades futuras
- Prefira soluções simples e específicas até que a necessidade real surja
3. DRY: eliminando duplicação de código
A duplicação de código pode assumir várias formas: código literal idêntico, lógica de negócio repetida, documentação inconsistente e configuração espalhada. Aplicar DRY reduz erros e facilita a manutenção.
Exemplo de violação de DRY:
# Duplicação de lógica de validação de e-mail em múltiplos lugares
def validar_usuario(email):
if '@' not in email or '.' not in email.split('@')[-1]:
raise ValueError("Email inválido")
# ... resto da lógica
def validar_pedido(email_cliente):
if '@' not in email_cliente or '.' not in email_cliente.split('@')[-1]:
raise ValueError("Email inválido")
# ... resto da lógica
Solução DRY:
def validar_email(email):
if '@' not in email or '.' not in email.split('@')[-1]:
raise ValueError("Email inválido")
return True
def validar_usuario(email):
validar_email(email)
# ... resto da lógica
def validar_pedido(email_cliente):
validar_email(email_cliente)
# ... resto da lógica
Cuidados importantes:
- Duplicação acidental: código que parece igual mas serve a propósitos diferentes
- Duplicação essencial: repetição necessária por questões de performance ou acoplamento
4. Conflitos entre YAGNI e DRY na prática
O conflito clássico surge quando decidimos abstrair cedo demais (violando YAGNI) ou repetir código (violando DRY). A Regra dos Três (Rule of Three) oferece um ponto de equilíbrio: só abstraia quando o código se repetir três vezes.
Análise de custo-benefício:
# Primeira ocorrência: implementação direta (YAGNI)
def calcular_frete_simples(peso):
return peso * 5.0
# Segunda ocorrência: ainda aceitável repetir (YAGNI)
def calcular_frete_expresso(peso):
return peso * 5.0 + 10.0
# Terceira ocorrência: momento de abstrair (DRY)
def calcular_frete(peso, tipo='simples'):
base = peso * 5.0
return base + (10.0 if tipo == 'expresso' else 0.0)
5. Exemplos práticos de aplicação combinada
Estudo de caso: sistema de e-commerce com regras de desconto
Cenário inicial (YAGNI aplicado):
class Pedido:
def __init__(self, itens, cliente):
self.itens = itens
self.cliente = cliente
def calcular_total(self):
total = sum(item.preco * item.quantidade for item in self.itens)
# Desconto simples para clientes VIP
if self.cliente.tipo == 'vip':
total *= 0.9
return total
Quando surgem novas regras (DRY aplicado):
class CalculadoraDesconto:
@staticmethod
def aplicar(regra, total):
if regra == 'vip':
return total * 0.9
elif regra == 'primeira_compra':
return total * 0.85
elif regra == 'fidelidade':
return total * 0.95
return total
class Pedido:
def __init__(self, itens, cliente):
self.itens = itens
self.cliente = cliente
def calcular_total(self):
total = sum(item.preco * item.quantidade for item in self.itens)
return CalculadoraDesconto.aplicar(self.cliente.regra_desconto, total)
Refatoração segura em código legado:
# Código legado com duplicação
def processar_pagamento_cartao(valor, parcelas):
taxa = 0.03 if parcelas <= 6 else 0.05
return valor * (1 + taxa)
def processar_pagamento_boleto(valor):
taxa = 0.02
return valor * (1 + taxa)
# Refatoração aplicando DRY sem violar YAGNI
def calcular_taxa_pagamento(tipo, valor, parcelas=1):
taxas = {
'cartao': 0.03 if parcelas <= 6 else 0.05,
'boleto': 0.02
}
return valor * (1 + taxas.get(tipo, 0))
6. Ferramentas e técnicas para manter o equilíbrio
Para aplicar YAGNI e DRY de forma consistente, utilize:
- Análise estática: ferramentas como SonarQube detectam duplicação de código (DRY) e complexidade excessiva (possível violação de YAGNI)
- Revisão de código: Checklists focados em código morto e abstrações prematuras
- Métricas: Acompanhe acoplamento e coesão para avaliar se abstrações são justificadas
- Testes: Testes unitários ajudam a identificar quando a duplicação se torna problemática
7. Conclusão e boas práticas recomendadas
O equilíbrio entre YAGNI e DRY depende do contexto: tamanho da equipe, prazo, maturidade do projeto e frequência de mudanças esperadas.
Checklist para decisões:
- A funcionalidade é requisitada agora? (Se não, YAGNI manda não implementar)
- O código se repete três vezes ou mais? (Se sim, considere DRY)
- A abstração proposta reduzirá a complexidade? (Se não, evite)
- A equipe entende a abstração? (Se não, prefira simplicidade)
Integrar YAGNI e DRY como filosofia de desenvolvimento ágil significa reconhecer que software é um artefato vivo, que deve evoluir organicamente. Comece simples, repita quando necessário e abstraia apenas quando o custo da duplicação superar o custo da abstração.
Referências
- The Pragmatic Programmer: DRY Principle — Capítulo original sobre o princípio Don't Repeat Yourself, com exemplos e discussões sobre duplicação de conhecimento.
- Extreme Programming Explained: YAGNI — Livro de Kent Beck que introduz o princípio You Aren't Gonna Need It no contexto do XP.
- Martin Fowler: Rule of Three — Artigo clássico sobre quando aplicar refatoração baseada na repetição de código.
- Refactoring Guru: DRY vs YAGNI — Guia prático com exemplos de código sobre quando usar cada princípio.
- SonarQube Documentation: Duplications — Documentação oficial sobre detecção de duplicação de código e métricas relacionadas.
- Clean Code: A Handbook of Agile Software Craftsmanship — Robert C. Martin discute a aplicação prática de YAGNI e DRY em capítulos sobre funções e classes.