Projetar para evolução não é adivinhar o roadmap dos próximos cinco anos. Já vi abstrações “flexíveis” demais tornarem uma feature simples quase ilegível. O objetivo é outro: deixar as decisões caras bem protegidas e as decisões provisórias fáceis de trocar.
Uma mudança atravessa interface, backend, dados e integrações. Se apenas uma dessas partes aceita transição segura, o sistema continua rígido.
Estável não quer dizer congelado
Busco estabilidade na intenção. Um módulo de notificações continua enviando mensagens mesmo que o canal mude de e-mail para WhatsApp. Se o domínio conhece payloads e erros específicos do fornecedor, uma troca operacional invade o produto inteiro.
Contratos pequenos ajudam. Contratos genéricos que tentam prever todos os canais imagináveis só movem a complexidade de lugar.
Coesão reduz o estrago
Código que muda pela mesma razão deveria morar perto. Um módulo precisa controlar seus casos de uso e dados, expondo o necessário sem abrir seus detalhes internos.
- Responsabilidade: fica claro quem toma cada decisão.
- Contrato: consumidores dependem de comportamento público.
- Compatibilidade: versões antiga e nova convivem durante a mudança.
- Observação: métricas mostram se a transição funcionou.
Dados mudam em produção
Prefiro migrações em etapas: adicionar a nova estrutura, publicar código compatível, migrar dados e só então remover o formato antigo. Esse cuidado parece lento no papel, mas é muito mais rápido que recuperar um banco após uma alteração irreversível.
APIs e eventos pedem o mesmo respeito. Adicionar campo costuma ser simples; mudar silenciosamente o significado de um campo não é.
Integrações não seguem nosso calendário
Na ANIMAPS, módulos bem definidos permitiram ampliar o produto sem uma refatoração geral a cada entrega. Em trabalhos com Sinaxys e Entrega Contínua, Stripe, N8N e WhatsApp trouxeram outra lição: fornecedores mudam, falham e respondem em ritmos próprios.
Autenticação, idempotência e tradução ficam nos adaptadores. O caso de uso deveria pedir “enviar mensagem” ou “iniciar cobrança”, não conhecer o manual do SDK.
Arquitetura evolutiva não prevê o futuro. Ela torna suportável descobrir que estávamos errados.
Feedback fecha o ciclo
Testes protegem regras e contratos em níveis diferentes. Logs, métricas e traces mostram o que os testes não conseguem prever. Também acompanho sinais menos glamourosos: quantos módulos uma alteração toca? Quanto tempo levamos para entender uma regressão?
Diagramas podem parecer impecáveis enquanto toda entrega exige coordenação geral. O histórico de mudanças costuma ser um crítico mais honesto.
Conclusão
Projetar para evolução é combinar limites claros, transições compatíveis e feedback rápido. Não precisamos preparar cada extensão possível. Precisamos reconhecer o que é difícil de reverter, conter o que varia e permitir que o sistema mude sem uma aposta de tudo ou nada.