Desacoplamento real: o que a mão direita faz a esquerda não precisa saber
A maioria dos projetos que eu vejo travar não tem bug complexo. Tem acoplamento. Alguém escreveu código onde um módulo conhece os detalhes internos de outro, e aí tudo vira uma teia que ninguém consegue mexer sem quebrar três coisas em outro lugar.
o que a mão direita faz a esquerda não precisa saber
O princípio é simples na teoria e todo mundo ignora na prática. Significa que cada parte do sistema deve operar com base em interfaces definidas, não nos detalhes da implementação da outra parte. Se a mão direita muda o formato do objeto que entrega, a esquerda não pode simplesmente parar de funcionar porque estava lendo campos direto do objeto bruto. Eu trabalhei num sistema de pagamentos há uns anos onde o módulo de cobrança tinha uma dependência explícita da estrutura de resposta do gateway Stripe. Quando o Stripe atualizou a API e mudou o campo customer.id para customer.cus_xxx, o módulo de cobrança quebrou de repente. Mas o problema maior não foi esse. Foi que eu precisei caçar por toda a base porque vários outros módulos — relatório financeiro, dashboard de retenção, sistema de email — também estavam consumindo diretamente o objeto do Stripe sem nenhuma camada de abstração.
O workaround que eu fiz foi criar um adaptador. Um serviço intermediário que consumia a API externa, mapeava os campos para um domínio interno limpo, e o resto do sistema passava a trabalhar só com esse domínio. Demorou cerca de dois dias de trabalho isolado. Antes disso, seria necessário revisar dezenas de arquivos espalhados por múltiplas equipes. O conceito por trás disso se chama Dependency Inversion Principle. Módulos de alto nível não devem depender de módulos de baixo nível. Ambos devem depender de abstrações. E abstrações não devem depender de detalhes. Detalhes devem depender de abstrações.
Na prática, isso se traduz em três coisas que eu sempre sigo: Primeiro, definir contratos claros antes de escrever implementação. Um contrato pode ser uma interface TypeScript, um protocol Swift, um tipo anotado em Python com type hints rigorosos, ou até um schema JSON validado. O importante é que ele exista de forma independente do código que o implementa.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Segundo, nunca deixar que um módulo externo exponha sua estrutura interna como parte do contrato público. Se você está usando uma biblioteca de terceiros, crie um wrapper que traduza os dados dela para o seu domínio. Custa um pouco mais de código no começo, mas evita que uma atualização da biblioteca derrube meia aplicação. Terceiro, testar as fronteiras entre módulos de forma isolada. Se o seu módulo A passa dados para o módulo B, teste o que acontece quando B recebe dados válidos, inválidos e ausentes. Você vai perceber gargalos que não aparecem em testes unitários isolados.
O lado chato é que desacoplamento exige disciplina. Você precisa escrever código extra que não é "diretamente útil". Interfaces, adapters, mappers, factories — tudo isso é sobrecarga que não gera funcionalidade aparente. Eu já vi equipe inteira rejeitar esse padrão porque "é muito boilerplate". A verdade é que o boilerplate é o preço de não passar madrugadas debugando quebras em cascata. Outra armadilha comum é o que eu chamo de acoplamento disfarçado. A pessoa cria a interface, mas preenche ela com dados que só fazem sentido na implementação específica. Tipo definir um DTO com campos como stripeCustomerId ou awsRegion. Isso é acoplamento mascarado de abstração. O nome do campo já revela a dependência. Use nomes neutros do domínio: clientId, region, providerReference.
Também tem o caso onde o desacoplamento vai longe demais e vira overengineering. Não adianta criar uma interface para tudo se o sistema tem três telas e nada vai reutilizar. Avalie o tamanho do sistema, a probabilidade de mudança, e o custo real de manutenção antes de decidir o nível de abstração. Desacoplamento é uma ferramenta, não uma religião. Se o seu projeto já está muito acoplado e você não consegue refatorar tudo de uma vez, uma tática que funciona é o strangler pattern. Vá isolando module por module, criando adaptadores progressivos, enquanto o sistema legado continua rodando. É mais devagar, mas não para a produção.
O que eu posso afirmar com segurança é que projetos que mantêm essa separação tendem a ter uma curva de onboarding mais suave, testes mais confiáveis, e mudanças que ficam contidas em áreas específicas. Projetos que ignoram isso geralmente chegam num ponto onde qualquer modificação simples exige uma revisão de segurança em tudo que está conectado.