DRY, KISS e YAGNI: os princípios que todo dev deve conhecer

1. Introdução aos três pilares da simplicidade

1.1. O que são DRY, KISS e YAGNI e por que são fundamentais na Arquitetura de Software

DRY (Don't Repeat Yourself), KISS (Keep It Simple, Stupid) e YAGNI (You Ain't Gonna Need It) formam a tríade de princípios que orientam decisões arquiteturais rumo à simplicidade e manutenibilidade. DRY combate a duplicação de conhecimento, KISS defende soluções diretas e YAGNI previne implementações prematuras. Juntos, eles atuam como filtros contra a complexidade acidental — aquela que introduzimos sem necessidade real.

1.2. A relação entre esses princípios e a redução de complexidade acidental

Complexidade acidental surge quando a solução técnica é mais intrincada que o problema de negócio. DRY reduz a dispersão de lógica, KISS elimina adornos desnecessários e YAGNI corta funcionalidades especulativas. Um sistema que respeita esses três princípios tende a ter menor custo de mudança e maior previsibilidade.

1.3. Como eles se complementam (e às vezes se contradizem) no design de sistemas

Embora complementares, esses princípios podem entrar em conflito. DRY pode levar a abstrações complexas que violam KISS. YAGNI pode sugerir duplicação temporária que contradiz DRY. O equilíbrio exige análise contextual: domínio, frequência de mudança e maturidade do time determinam qual princípio deve prevalecer em cada decisão.

2. DRY (Don't Repeat Yourself) – A luta contra a duplicação

2.1. Definição formal: cada conhecimento deve ter uma representação única, não ambígua e autoritativa

DRY afirma que toda peça de conhecimento deve ter uma representação única dentro do sistema. Isso não se limita a código — inclui documentação, configurações e regras de negócio. Violações geram inconsistências: corrigir um bug em um local e esquecer outro é a consequência mais comum.

2.2. Armadilhas comuns: duplicação acidental vs. duplicação intencional

Nem toda repetição é violação de DRY. Duplicação acidental ocorre quando código similar surge por coincidência, mas representa conceitos diferentes. Duplicação intencional pode ser válida quando contextos de negócio distintos exigem evolução independente.

// Duplicação acidental (candidata a DRY)
function calcularImpostoVenda(valor) { return valor * 0.1; }
function calcularTaxaServico(valor) { return valor * 0.1; }

// Duplicação intencional (contextos diferentes)
function calcularImpostoVenda(valor) { return valor * 0.1; }
function calcularTaxaServico(valor) { return valor * 0.05; } // regra diferente

2.3. Aplicação arquitetural: extração de serviços, bibliotecas compartilhadas e abstrações mal planejadas

Em arquitetura, DRY justifica serviços compartilhados, bibliotecas internas e módulos reutilizáveis. Porém, extrair um serviço comum antes de existirem três consumidores distintos é abstração prematura. O custo de acoplamento introduzido pode superar o benefício da eliminação de duplicação.

3. KISS (Keep It Simple, Stupid) – A simplicidade como meta de design

3.1. O princípio da navalha de Occam aplicado a componentes e módulos

KISS aplica a navalha de Occam à engenharia de software: entre duas soluções equivalentes, a mais simples é preferível. Componentes com responsabilidade única, interfaces enxutas e fluxos lineares são manifestações desse princípio. Um módulo que faz uma coisa bem é mais fácil de testar, depurar e modificar.

3.2. Como evitar over-engineering: soluções simples vencem soluções genéricas prematuras

Over-engineering é o resultado de antecipar requisitos que nunca chegam. Um sistema de plugins para três variações de notificação quando apenas e-mail é necessário é um exemplo clássico. A simplicidade deve ser a default, e a complexidade, uma concessão justificada.

// Solução KISS: direta e específica
function enviarEmail(destinatario, assunto, corpo) {
    // implementação direta
}

// Solução over-engineered: genérica e complexa
function notificar(usuario, tipo, template, canais) {
    // fábrica de notificações, roteamento, fallback
}

3.3. Trade-offs entre simplicidade e flexibilidade futura

Monolitos são mais simples que microsserviços, mas menos flexíveis. A decisão depende do ciclo de vida do projeto: um MVP deve priorizar KISS; um sistema maduro com times independentes pode justificar a complexidade adicional. O trade-off é sempre contextual.

4. YAGNI (You Ain't Gonna Need It) – O custo da antecipação

4.1. Definição: nunca implementar funcionalidades antes de serem realmente necessárias

YAGNI ensina que implementar algo "por precaução" gera custo imediato (desenvolvimento, teste, manutenção) com benefício incerto. Funcionalidades especulativas tendem a nunca ser usadas ou, quando usadas, precisam ser refeitas porque os requisitos mudaram.

4.2. O paradoxo da previsibilidade: quando YAGNI colide com requisitos não funcionais

YAGNI não significa ignorar requisitos arquiteturais óbvios. Logging, tratamento de erros e segurança não são "funcionalidades" — são requisitos não funcionais essenciais. O paradoxo surge quando a antecipação é necessária para viabilidade técnica futura (ex.: escalabilidade horizontal desde o início).

4.3. Estratégias para prototipação rápida e entrega incremental

Aplicar YAGNI não significa ausência de planejamento. Use prototipação rápida para validar hipóteses, entregue incrementos funcionais e refatore quando a necessidade real surgir. A arquitetura deve permitir evolução sem reescritas completas.

// YAGNI violado: cache desnecessário
function buscarUsuario(id) {
    if (cache.existe(id)) return cache.obter(id);
    var usuario = banco.buscar(id);
    cache.armazenar(id, usuario);
    return usuario;
}

// YAGNI respeitado: implementação direta
function buscarUsuario(id) {
    return banco.buscar(id);
}

5. Tensões e sinergias entre DRY, KISS e YAGNI

5.1. DRY vs. KISS: quando abstrair demais para evitar repetição torna o sistema mais complexo

Uma abstração que unifica três casos similares, mas requer parâmetros de configuração complexos e condicionais internos, viola KISS em nome de DRY. A simplicidade do consumidor (chamada única) esconde complexidade do provedor.

5.2. YAGNI vs. DRY: como a duplicação temporária pode ser mais simples que uma abstração prematura

Duplicar código em dois contextos que podem divergir é melhor que criar uma abstração genérica prematura. Quando os contextos convergirem (terceira ocorrência), a abstração natural será mais adequada.

5.3. Critérios de decisão: contexto do domínio, frequência de mudança e maturidade do time

Três perguntas ajudam a decidir: (1) Este código representa o mesmo conceito de negócio? (2) Com que frequência muda? (3) O time consegue manter a abstração? Respostas negativas favorecem duplicação temporária (YAGNI + KISS) sobre DRY.

6. Aplicação prática na Arquitetura de Software

6.1. Exemplo em design de APIs: evitar duplicação de lógica de validação sem criar abstrações frágeis

// Sem DRY: validação duplicada
POST /usuario { validarEmail(email); validarCPF(cpf); }
POST /pedido { validarEmail(email); validarCPF(cpf); }

// Com DRY: validação centralizada, mas simples
POST /usuario { validarDadosPessoaFisica(dados); }
POST /pedido { validarDadosPessoaFisica(dados); }

6.2. Exemplo em camadas de serviço: quando extrair um serviço comum

Extraia um serviço comum apenas quando houver três consumidores com requisitos idênticos. Até lá, duplicação controlada (com documentação explícita) é mais KISS e YAGNI.

6.3. Exemplo em bancos de dados: normalização (DRY) vs. desnormalização por performance

Normalização é DRY aplicado a dados. Desnormalização sacrifica DRY por performance (KISS para consultas). A decisão depende do padrão de acesso: se leituras dominam, desnormalizar pode ser a escolha KISS.

7. Antipadrões e erros comuns

7.1. O "DRY extremo": abstrações genéricas que escondem complexidade

Uma classe RepositorioGenerico<T> com reflexão, queries dinâmicas e tratamento de exceções genérico é DRY no código, mas KISS violado. Cada consumidor paga o custo cognitivo da complexidade.

7.2. O "YAGNI míope": ignorar requisitos arquiteturais óbvios

Não implementar logging, monitoramento ou tratamento de erros padronizado "porque YAGNI" é negligência. Esses não são funcionalidades especulativas — são infraestrutura necessária.

7.3. O "KISS ingênuo": soluções simplistas que geram dívida técnica acumulada

Evitar toda abstração leva a código procedural, funções gigantes e ausência de separação de concerns. KISS não significa ausência de design — significa design direto e sem complicação desnecessária.

8. Conclusão: como equilibrar os três princípios no dia a dia

8.1. Checklist prático para decisões arquiteturais

  1. A duplicação é acidental ou intencional? (DRY)
  2. A solução mais simples resolve o problema atual? (KISS)
  3. Essa funcionalidade é necessária agora? (YAGNI)
  4. Qual o custo de mudança futura versus custo imediato?

8.2. A importância do contexto: domínio, time e ciclo de vida

Projetos maduros com times experientes toleram mais abstrações (DRY). Startups em fase inicial devem priorizar KISS e YAGNI. Domínios complexos exigem mais DRY que domínios simples.

8.3. Relação com outros princípios

DRY, KISS e YAGNI conversam diretamente com SOLID (especialmente Single Responsibility e Interface Segregation), Lei de Demeter (baixo acoplamento) e coesão. Juntos, formam a base para decisões arquiteturais pragmáticas e sustentáveis.

O equilíbrio entre esses três princípios não é fórmula matemática — é habilidade adquirida com prática, reflexão e feedback contínuo. A boa arquitetura não é a mais elegante teoricamente, mas a que resolve o problema real com o menor custo de manutenção ao longo do tempo.

Referências