Princípios de Design de Software: KISS, DRY, YAGNI e o Equilíbrio de Coesão e Acoplamento
Da sobre-engenharia à simplicidade intencional: como aplicar os axiomas KISS, DRY, YAGNI e dominar a métrica fundamental de coesão versus acoplamento na sustentabilidade de sistemas corporativos.
No ciclo de vida de qualquer sistema de software, a entropia é inexorável. Conforme novos requisitos de negócio chegam e prazos apertam, bases de código tendem a se degradar caso a equipe não compartilhe princípios de engenharia claros. Frequentemente, a deterioração do código ocorre não pela ausência de padrões, mas pelo excesso de abstrações especulativas — a armadilha clássica da sobre-engenharia (overengineering).
Para construir sistemas sustentáveis, a engenharia de software consolidou um conjunto de axiomas pragmáticos: KISS (Keep It Simple, Stupid), DRY (Don't Repeat Yourself) e YAGNI (You Ain't Gonna Need It), balanceados pela constante busca por alta coesão e baixo acoplamento.
O Equilíbrio Fundamental: Coesão vs. Acoplamento
A métrica primária que determina a manutenibilidade de qualquer módulo, classe ou microsserviço repousa sobre a relação entre coesão e acoplamento:
flowchart TD
subgraph Ideal["Design Ideal: Módulos Autônomos"]
direction LR
subgraph M1["Módulo A (Alta Coesão)"]
A1["Função 1"] <--> A2["Função 2"]
A2 <--> A3["Função 3"]
end
subgraph M2["Módulo B (Alta Coesão)"]
B1["Serviço 1"] <--> B2["Serviço 2"]
end
M1 <--"Contrato Estável (Baixo Acoplamento)"--> M2
end
Coesão: Responsabilidades Focadas
A coesão mede o grau em que as responsabilidades internas de um módulo pertencem logicamente juntas.
- Baixa Coesão (Anti-pattern): Uma classe utilitária "God Class" (
CommonUtilsouManager) que valida CPF, envia e-mails, calcula impostos e gera planilhas Excel. Ela muda constantemente por razões não relacionadas. - Alta Coesão: Um componente
TaxCalculatorque contém unicamente algoritmos tributários e suas regras fiscais. Todas as suas funções e variáveis operam sobre o mesmo domínio restrito.
Acoplamento: O Grau de Interdependência
O acoplamento mede a dependência direta entre módulos distintos.
- Alto Acoplamento (Fragilidade): Módulo A acessa diretamente variáveis de instância privadas de Módulo B, ou conhece detalhes de implementação do banco de dados utilizado por B. Qualquer alteração em B quebra A em cascata (Shotgun Surgery).
- Baixo Acoplamento: Módulo A comunica-se com Módulo B estritamente através de contratos de interface pública ou mensagens assíncronas. B pode reescrever sua camada de persistência inteira sem que A sofra impacto.
Separação de Preocupações (SoC) e Preocupações Transversais
O princípio de Separação de Preocupações (Separation of Concerns - SoC) postula que um programa deve ser dividido em seções distintas, onde cada seção aborda uma preocupação específica de computação:
- Preocupações de Negócio / Domínio: Regras de cálculo de juros, validação de limites de crédito, workflow de pedidos.
- Preocupações de Infraestrutura: Conexão com banco de dados, chamadas HTTP para parceiros externos, serialização JSON.
- Preocupações Transversais (Cross-Cutting Concerns): Funcionalidades que cortam horizontalmente múltiplos módulos da aplicação, tais como:
- Autenticação e autorização de segurança
- Registro de logs e rastreamento distribuído (tracing)
- Auditoria e conformidade (LGPD/GDPR)
- Cache e tratamento global de exceções
flowchart TB
subgraph CrossCutting["Preocupações Transversais (Cross-Cutting Concerns)"]
Auth["Segurança / Autenticação"]
Log["Logging / Rastreamento"]
Cache["Resiliência / Caching"]
end
subgraph Camadas["Camadas de Domínio e Negócio"]
Ped["Módulo de Pedidos"]
Fin["Módulo Financeiro"]
Est["Módulo de Estoque"]
end
Auth -.-> Camadas
Log -.-> Camadas
Cache -.-> Camadas
Tais preocupações transversais não devem ser duplicadas no interior dos métodos de negócio. Em vez disso, devem ser abstraídas usando Aspect-Oriented Programming (AOP), Middlewares, Decorators ou Service Meshes.
Os Três Axiomas do Pragmatismo: KISS, DRY e YAGNI
| Princípio | Definição Central | Armadilha de Interpretação | Aplicação Pragmática |
|---|---|---|---|
| KISS (Keep It Simple, Stupid) | A simplicidade deve ser o objetivo primário de design; sistemas funcionam melhor se mantidos simples em vez de tornados complexos. | Confundir código simples com código ingênuo ou desestruturado. | Escrever código que um desenvolvedor júnior consiga ler em 5 minutos sem diagramas auxiliares. |
| DRY (Don't Repeat Yourself) | Todo conhecimento ou regra de negócio deve ter uma representação única, não ambígua e definitiva no sistema. | Tratar linhas de código idênticas por coincidência como duplicidade de conhecimento. | Unificar a regra de validação de CPF em um Value Object; aceitar repetição de código sintático se os domínios forem diferentes. |
| YAGNI (You Ain't Gonna Need It) | Nunca adicione funcionalidade até que ela seja necessária no presente momento. | Negligenciar flexibilidade arquitetural mínima (como interfaces para I/O). | Não criar motores de plugins genéricos quando o cliente solicitou apenas suporte a um formato fixo de arquivo. |
O Perigo do DRY Mal Aplicado (Acoplamento Acidental)
A falácia mais comum em times de software é aplicar DRY puramente em nível de sintaxe. Se dois microsserviços (ex.: Faturamento e Gestão de Clientes) possuem um método com 5 linhas similares para formatar um endereço, agrupá-los em uma biblioteca compartilhada acopla os dois times ao ciclo de vida daquela dependência.
Regra de Ouro: A duplicação de código é muito mais barata do que a abstração errada. DRY aplica-se à duplicação de intenção de negócio, não a semelhanças estruturais acidentais.
Requisitos Funcionais vs. Não Funcionais na Arquitetura
O design de software é o mecanismo de transição entre o levantamento de requisitos de produto e o sistema em operação:
flowchart LR
subgraph Requisitos["Engenharia de Requisitos"]
RF["Requisitos Funcionais<br/>(O que o sistema faz: Emitir Nota, Cancelar Pedido)"]
RNF["Requisitos Não Funcionais<br/>(Como o sistema opera: Latência < 50ms, 99.99% Uptime)"]
end
subgraph Decisoes["Decisões de Design de Software"]
Pat["Padrões de Projeto & Estilos de Arquitetura"]
Infra["Dimensionamento e Escolha de Bancos de Dados"]
end
RF --> Pat
RNF --> Infra
RNF --> Pat
- Requisitos Funcionais (RF): Definem o comportamento do sistema diante de entradas específicas. São guiados diretamente pelas histórias de usuário e casos de uso do negócio.
- Requisitos Não Funcionais (RNF): Também conhecidos como atributos de qualidade (desempenho, escalabilidade, segurança, portabilidade, testabilidade e disponibilidade). Um sistema pode atender 100% dos seus requisitos funcionais e ainda assim falhar miseravelmente se não atender aos atributos de latência ou segurança.
O Ciclo de Mudança Arquitetural (Architecture Change Cycle)
A arquitetura de software não é estática; ela evolui em ciclos contínuos de inspeção e adaptação:
flowchart TD
A["1. Identificação da Tensão<br/>(Novo requisito, gargalo de escala ou débito técnico)"] --> B["2. Análise de Trade-offs<br/>(Simplicidade vs. Flexibilidade, Custo vs. Performance)"]
B --> C["3. Refatoração Cirúrgica<br/>(Preservação de testes unitários e contratos públicos)"]
C --> D["4. Verificação por Testes de Caracterização<br/>(Garantia de não-regressão de comportamento)"]
D --> E["5. Documentação da Decisão<br/>(Architecture Decision Record - ADR)"]
E --> A
Ao documentar decisões técnicas via ADRs (registros de decisões arquiteturais), a equipe impede a regressão para decisões ingênuas e comunica explicitamente as premissas que justificaram a escolha de design.
Conclusão
Projetar software de alto nível não significa demonstrar erudição através de designs complexos com dezenas de camadas indiretas. A verdadeira maturidade da engenharia consiste em entregar soluções robustas que resolvem o problema essencial do usuário com o mínimo de código indispensável. Ao calibrar rigorosamente o acoplamento, buscar alta coesão e subordinar todas as decisões de código ao pragmatismo de KISS, DRY e YAGNI, sua equipe constrói sistemas que permanecem fáceis de alterar, testar e operar por décadas.