DRY vs. WET: quando copiar código é aceitável

1. O Dilema Clássico: DRY vs. WET

No universo do desenvolvimento de software, poucos princípios geram debates tão acalorados quanto DRY (Don't Repeat Yourself) e sua contraparte irônica WET (Write Everything Twice ou We Enjoy Typing). O princípio DRY foi cunhado por Andrew Hunt e David Thomas no livro The Pragmatic Programmer (1999), onde estabelece que "cada pedaço de conhecimento deve ter uma representação única, não ambígua e autoritativa dentro de um sistema".

A ideia é nobre: evitar duplicação de lógica reduz bugs, facilita manutenção e centraliza mudanças. No entanto, o fanatismo por DRY pode gerar abstrações prematuras e acoplamento desnecessário. Código que parece idêntico hoje pode representar conceitos de negócio completamente diferentes amanhã. O resultado? Abstrações frágeis que quebram em cascata quando um dos contextos evolui.

WET, por outro lado, é frequentemente tratado como anti-padrão, mas representa uma abordagem pragmática: escrever código duplicado de forma consciente quando a abstração custa mais do que a repetição.

2. Os Custos Ocultos da Abstração Forçada

Forçar DRY onde não cabe introduz complexidade acidental. Considere este exemplo:

# Abstração forçada (DRY mal aplicado)
def calcular_valor(produto, regiao, cliente_tipo):
    if regiao == "SP":
        imposto = produto.base * 0.18
    elif regiao == "RJ":
        imposto = produto.base * 0.20

    if cliente_tipo == "VIP":
        desconto = produto.base * 0.10
    elif cliente_tipo == "normal":
        desconto = 0

    return produto.base + imposto - desconto

Aqui, a função tenta servir dois contextos diferentes (cálculo para SP e RJ, com regras de desconto distintas). Quando a legislação de SP mudar, o código do RJ pode ser afetado. O paradoxo da reutilização surge: ninguém ousa modificar essa função porque ela "funciona para tudo", mas na verdade funciona mal para tudo.

Dependências indesejadas aparecem quando duas funcionalidades que parecem iguais evoluem em direções opostas. O custo de desacoplar uma abstração mal feita é frequentemente maior que o custo de ter mantido o código duplicado.

3. Quando Copiar Código é a Escolha Correta

Existem cenários onde WET é a abordagem superior:

Estabilização de sistemas legados: Refatorar código crítico que funciona perfeitamente há anos pode introduzir riscos desnecessários. Se o sistema legado processa pagamentos e está estável, copiar um trecho para um novo módulo pode ser mais seguro que modificar o original.

Prototipação e experimentação: Em fases de descoberta, velocidade importa mais que pureza arquitetural. Duplicar código permite testar hipóteses rapidamente sem o overhead de criar abstrações que podem ser descartadas.

Contextos com regras de negócio divergentes: Mesma lógica, significados diferentes:

# Cópia intencional - estados com regras fiscais independentes
def calcular_icms_sp(valor):
    # SP: alíquota 18%, base dupla
    base = valor * 2
    return base * 0.18

def calcular_icms_rj(valor):
    # RJ: alíquota 20%, base simples com dedução
    base = valor * 1.5
    deducao = 50 if valor > 1000 else 0
    return (base - deducao) * 0.20

Aqui, copiar foi a decisão correta: as regras de SP e RJ mudam em momentos diferentes e por razões diferentes. Uma abstração comum criaria um monstro de parâmetros condicionais.

4. Os Limites do WET: Quando a Cópia se Torna um Problema

WET tem custos reais que precisam ser gerenciados:

Efeito cascata de correções: Um bug de validação de CPF copiado em 10 lugares precisa ser corrigido 10 vezes. Em situações de emergência, isso pode significar deploys atrasados ou correções esquecidas.

Inconsistência silenciosa: Cópias que divergem sutilmente ao longo do tempo criam bugs intermitentes. Um validador de email que aceita "+" em um módulo mas rejeita em outro gera frustração para usuários.

Onboarding catastrófico: Novos desenvolvedores enfrentam um labirinto de código duplicado sem padrões claros. A curva de aprendizado dobra quando cada módulo tem sua própria versão de uma funcionalidade comum.

5. Estratégias para Decidir: DRY ou WET?

A regra dos três: Só abstraia após a terceira ocorrência. A primeira cópia é aprendizado, a segunda é confirmação do padrão, a terceira justifica a abstração.

Análise de taxa de mudança: Se duas cópias mudam sempre juntas (mesmo pull request, mesmo deploy), una-as. Se mudam separadamente (diferentes sprints, diferentes times), mantenha-as separadas.

Testes como bússola: Código duplicado é aceitável se for coberto por testes independentes e claros:

# Teste para cada contexto duplicado
def test_calcular_icms_sp():
    assert calcular_icms_sp(1000) == 360.0

def test_calcular_icms_rj():
    assert calcular_icms_rj(1000) == 285.0

Testes independentes permitem que cada cópia evolua sem quebrar as outras.

6. Padrões de Compromisso: Abstrações Flexíveis e Cópias Seguras

Template Method: Quando a estrutura é igual, mas o comportamento varia:

class CalculadoraImposto:
    def calcular(self, valor):
        base = self._calcular_base(valor)
        aliquota = self._obter_aliquota()
        return base * aliquota

class CalculadoraSP(CalculadoraImposto):
    def _calcular_base(self, valor):
        return valor * 2
    def _obter_aliquota(self):
        return 0.18

class CalculadoraRJ(CalculadoraImposto):
    def _calcular_base(self, valor):
        return valor * 1.5
    def _obter_aliquota(self):
        return 0.20

Copy-paste com documentação explícita: Marcar cópias intencionais:

# CÓPIA INTENCIONAL - Regra SP (29/10/2023)
# Se modificar, verifique se RJ precisa da mesma alteração
def validar_cnpj_sp(documento):
    # ... lógica específica SP

Módulos de utilidade de domínio: Isole trechos genuinamente reutilizáveis (formatação de datas, validações universais) sem forçar acoplamento entre contextos de negócio.

7. Ferramentas e Práticas para Gerenciar a Tensão

Linters com tolerância configurável: SonarQube, PMD e jscpd permitem definir limites de duplicação aceitáveis. Configure thresholds diferentes para módulos core (DRY estrito) vs. módulos experimentais (WET tolerado).

Code reviews focados em intenção: Durante revisões, pergunte: "Essas cópias vão divergir no futuro?" Se a resposta for sim, aceite a duplicação. Se for não, exija abstração.

Refatoração incremental: Transforme WET em DRY apenas quando o custo da duplicação supera o custo da abstração. Monitore métricas como "tempo para corrigir bug" e "número de arquivos modificados por feature" para tomar decisões baseadas em dados.


O equilíbrio entre DRY e WET não é técnico — é contextual. Código não existe no vácuo; ele serve a domínios de negócio, prazos e equipes com dinâmicas próprias. A sabedoria está em saber quando a abstração une e quando ela amarra. Copiar código não é pecado quando feito com consciência, documentação e testes. O pecado é copiar sem pensar.

Referências