Vamos ser honestos. Quantas horas você já passou pesquisando no Google a "melhor forma" de fazer alguma coisa?
"Qual o melhor banco de dados para um e-commerce?"
"Devo utilizar Microsserviços ou Monolito?"
"Qual seria a 'melhor prática' para gerenciar estados no React?"
O ecossistema do desenvolvimento de software se mostra de certa forma obcecado pela ideia de "Boas Práticas". Elas são vendidas como regras de ouro, caminhos infalíveis para um software limpo, escalável e fácil de manter. Em code reviews, em artigos de blog, em conferências... estamos cercados pela "melhor forma" de construir software.
Mas e se essa busca for, na verdade, uma armadilha?
Aqui está o problema: quando aplicamos essas "regras" cegamente, muitas vezes o resultado é o oposto do esperado. Você implementa microsserviços para uma aplicação simples e, de repente, gasta 80% do tempo gerenciando uma infraestrutura de deploy complexa em vez de entregar valor para o usuário.
Recentemente, escrevi sobre "O Custo da Perfeição" (overengineering). Nele, explorei como nossa tendência de construir "fortalezas" tentando cobrir todos os cenários futuros possíveis muitas vezes leva a sistemas complexos, caros e difíceis de manter.
O que eu percebi é que o overengineering é, frequentemente, um sintoma. Mas qual é a causa raiz? Durante a leitura do livro "Arquitetura de Software: As partes difíceis" pude entender pelo menos parte do porquê disso.
O insight central do livro é ao mesmo tempo brutal e libertador: a verdadeira arquitetura de software eficiente não é sobre APENAS seguir regras ou boas práticas. É sobre entender, analisar e gerenciar tradeoffs (trocas e concessões).
Neste artigo, quero compartilhar os principais insights que tirei desse livro, focando em por que devemos parar de procurar a "bala de prata" e começar a desenvolver a habilidade mais importante de um arquiteto: analisar tradeoffs.
Quebrando o Mito: Por que "Boas Práticas" Podem ser Perigosas
O maior problema com o termo "boa prática" é que na maioria das vezes que vejo ele sendo utilizado vejo também ignorarem uma das palavras mais importantes da engenharia de software (se não a mais importante): contexto.
Uma solução nunca é boa ou ruim no vácuo. Ela é boa ou ruim para um problema específico, para uma equipe específica, em um momento específico.
O que é uma "boa prática" para a Netflix, com suas dezenas de milhares de microsserviços e uma cultura de engenharia de elite, é quase certamente uma péssima prática para uma startup de 5 pessoas tentando validar uma ideia de produto (MVP) nos próximos 3 meses.
Vamos pegar o exemplo mais clássico da última década: Microsserviços.
Se você ouvir as conferências e ler os blogs mais populares, a "boa prática" parece ser decompor tudo. A promessa é tentadora:
- Escalabilidade independente!
- Times autônomos!
- Deploy sem medo!
Mas isso é apenas um lado da moeda. O que o livro "Arquitetura de Software: As partes difíceis" enfatiza é que essa escolha não é uma melhoria, é um tradeoff.
Ao escolher microsserviços, você está trocando a simplicidade de desenvolvimento de um Monolito por uma complexidade operacional e de infraestrutura massiva.
O Tradeoff dos Microsserviços:
- Você GANHA: Escalabilidade granular, deploy independente, isolamento de falhas (talvez, na melhor das hipóteses com essa arquitetura sendo bem implementada).
- Você PAGA COM: Complexidade de rede (latência!), consistência de dados (sagas, anyone?), dificuldade de observabilidade (o que chamou o quê?), e a necessidade de um DevOps muito mais maduro.
A "boa prática" dos microsserviços só é "boa" se o problema que você tem (ex: dificuldade de escalar uma parte específica do seu sistema) for maior do que o novo conjunto de problemas que você vai ganhar (ex: gerenciar uma arquitetura distribuída complexa).
Para 90% das aplicações no início, o "custo" dos microsserviços supera em muito o "ganho".
Esse é o perigo: uma "boa prática" é apenas a solução que alguém encontrou para um tradeoff específico. Copiar a solução sem entender o tradeoff original é a receita para o desastre (ou, no mínimo, para muito overengineering, como falamos antes).
A Arquitetura como Análise de Tradeoffs
Se o nosso trabalho não é mais aplicar "boas práticas" cegamente, qual é a nossa função como desenvolvedores e arquitetos?
O livro deixa isso muito claro: nosso trabalho é analisar tradeoffs. Ele redefine a arquitetura de software de uma forma poderosa. Ela deixa de ser a busca por um estado final "perfeito" ou "correto" e passa a ser o processo contínuo de tomar decisões difíceis em um mar de ambiguidades.
A parte mais importante de entender é esta: um tradeoff não é uma escolha entre uma opção "boa" e uma "ruim". Isso seria fácil. Um tradeoff é quase sempre uma escolha entre duas (ou mais) opções ruins de formas diferentes, ou boas de formas diferentes.
Um tradeoff é escolher entre:
- Opção A: Rápida de desenvolver, mas difícil de escalar.
- Opção B: Lenta de desenvolver, mas fácil de escalar.
Qual é a "certa"? Depende. Você precisa lançar rápido (startup em MVP) ou você precisa aguentar milhões de usuários (empresa estabelecida)?
O livro argumenta que as "partes difíceis" da arquitetura (como decompor sistemas ou gerenciar dados distribuídos) são difíceis precisamente porque não existe uma resposta fácil. Tudo o que existe são concessões.
O papel do arquiteto ou do desenvolvedor sênior, portanto, muda drasticamente:
- De: "Saber qual é a melhor solução (ex: Kubernetes)."
- Para: "Saber quais são as perguntas certas a se fazer (ex: 'Quais são os custos operacionais de adotar Kubernetes agora vs. o custo de migrar para ele depois?')."
O livro nos ensina que o arquiteto não é aquele que tem a resposta mágica. É aquele que entende profundamente os requisitos de negócio e consegue mapear quais atributos de arquitetura (performance, manutenibilidade, escalabilidade, custo, etc.) são mais importantes para aquele contexto e quais podem ser sacrificados.
Três Exemplos Práticos de Tradeoffs
Tudo bem, a teoria de "analisar tradeoffs" é ótima. Mas como ela se parece no dia a dia? Vamos analisar alguns dilemas clássicos da engenharia de software sob a ótica do livro.
1. O Dilema dos Dados: Consistência vs. Disponibilidade
A pergunta "SQL ou NoSQL?" talvez seja a "boa prática" mais mal interpretada da última década. Nesse momento você não está diante de uma simples escolha de banco de dados; você está principalmente diante da escolha de um modelo de consistência.
- A "Boa Prática" (O Mito): "NoSQL escala melhor!" ou "SQL é ACID e confiável!"
- A Análise de Tradeoff: A verdadeira questão é: O que é mais caro para o seu negócio? Estar temporariamente inconsistente ou estar temporariamente indisponível?
O Tradeoff (Teorema CAP):
- Escolhendo Consistência Forte (ex: um banco SQL tradicional): Você GANHA a garantia de que os dados estão 100% corretos e atualizados em todo o sistema. Uma transação bancária sempre reflete o saldo real.
O Custo (Tradeoff): Você PAGA com disponibilidade. Em um sistema distribuído, se a rede falhar entre dois nós, o banco precisa travar a operação (ficar indisponível) para garantir que a consistência não seja violada.
Escolhendo Disponibilidade (ex: NoSQL com Consistência Eventual): Você GANHA um sistema que sempre responde. Um "like" no Instagram ou um item adicionado ao carrinho de compras funciona mesmo que parte do sistema esteja lenta.
O Custo (Tradeoff): Você PAGA com consistência. O sistema pode levar alguns milissegundos (ou segundos) para que todos os nós concordem. O seu "like" pode não
aparecer para outra pessoa imediatamente. Você pode vender um item do carrinho que acabou de ficar sem estoque (e terá que lidar com isso depois).
O Trabalho do Arquiteto: Não é saber se "SQL é melhor". É perguntar: "Para esta funcionalidade (ex: carrinho de compras vs. pagamento), o que é mais crítico: a disponibilidade (o cliente conseguir usar) ou a consistência (o dado estar 100% correto agora)?"
2. O Dilema da Comunicação: Síncrona vs. Assíncrona
Como os seus serviços (ou microsserviços) devem conversar?
- A "Boa Prática" (O Mito): "Desacople tudo! Use um Message Broker (Kafka/RabbitMQ) para tudo!"
- A Análise de Tradeoff: A comunicação assíncrona é poderosa, mas é incrivelmente complexa.
O Tradeoff (Acoplamento):
- Escolhendo Síncrona (ex: uma chamada de API REST direta): Você GANHA simplicidade e imediatismo. Você chama a API
ServicoB.FazerCoisa()e sabe imediatamente se funcionou ou falhou. O código é linear e fácil de debugar.O Custo (Tradeoff): Você PAGA com acoplamento temporal. Se o
ServicoBestiver lento ou cair, o seuServicoAtrava junto, esperando uma resposta. Uma falha se espalha em cascata.Escolhendo Assíncrona (ex: publicando um evento
CoisaFeita): Você GANHA resiliência. OServicoAapenas "grita" o evento e segue a vida. Se oServicoBestiver caído, ele não é afetado. OServicoBprocessa o evento quando voltar.O Custo (Tradeoff): Você PAGA com complexidade massiva. Como você monitora se o evento foi processado? Como lida com re-tentativas? E a ordem dos eventos? Você trocou a simplicidade de desenvolvimento por resiliência operacional.
O Trabalho do Arquiteto: "Para este fluxo de trabalho, o que é mais importante: a resiliência (assíncrono) ou a simplicidade de desenvolvimento e debug (síncrono)?"
3. O Dilema do Código: Desempenho vs. Manutenibilidade
Como você deve escrever aquele novo módulo?
- A "Boa Prática" (O Mito): "Clean Code! SOLID, DRY, KISS! Crie todas as abstrações corretas!"
- A Análise de Tradeoff: Cada camada de abstração que facilita a manutenção (o "Clean Code") tem um custo de desempenho.
O Tradeoff (Abstração):
- Escolhendo Manutenibilidade (Clean Code): Você GANHA um código fácil de testar, fácil de mudar e fácil de entender (novos devs agradecem). Você usa Repositórios, Casos de Uso, Injeção de Dependência, etc.
- O Custo (Tradeoff): Você PAGA com desempenho (mesmo que pequeno) e "boilerplate". Cada camada de abstração adiciona overhead (mais chamadas de função, mais alocação de memória, mais indireção).
- Escolhendo Desempenho (”quick and dirty”): Você GANHA velocidade bruta. Você escreve uma query SQL "crua" e otimizada que faz 5 joins direto no seu handler, pula o ORM e retorna os dados em 10ms.
- O Custo (Tradeoff): Você PAGA com manutenibilidade. Esse código é frágil, difícil de testar e um pesadelo para alterar. Se o schema do banco mudar, boa sorte.
O Trabalho do Arquiteto:
"Esta é uma parte crítica do sistema (hot path), como um loop de renderização de um jogo, onde cada nanossegundo conta? Ou é uma tela de CRUD de ‘configurações’ que é usada uma vez por mês e onde a velocidade de modificação é mais importante que a de execução?"
Como Começar a Pensar em Tradeoffs
A partir de agora, "depende" é sua nova resposta favorita. Mas como podemos transformar essa filosofia em uma ferramenta prática? Como se constrói esse "músculo" de análise de tradeoffs?
Aqui estão algumas ações práticas que você pode começar a aplicar hoje:
1. Mude o seu Vocabulário (e suas Perguntas)
O primeiro passo é o mais simples e o mais poderoso. Pare de perguntar "Qual é o melhor?" e comece a perguntar "Quais são os custos?".
Pare de perguntar: "Qual é o melhor banco de dados para esse projeto?"
Comece a perguntar: "Quais são os tradeoffs de usar PostgreSQL vs. MongoDB para este caso de uso específico? O que estamos otimizando? Velocidade de consulta, custo de infra, ou flexibilidade de esquema?"
Pare de dizer: "Deveríamos usar microsserviços porque é a 'boa prática'."
Comece a dizer: "Se usarmos microsserviços, ganharemos X, mas pagaremos com a complexidade Y. Esse ganho justifica esse custo... agora?"
2. Identifique os "Drivers" do Negócio
Você não pode pesar um tradeoff se não souber o que é mais importante na balança. A arquitetura sempre deve servir aos objetivos do negócio.
Antes de qualquer decisão técnica grande, pergunte ao seu time, PM ou líder:
"Qual é o requisito mais importante para esta funcionalidade?"
- É Velocidade de Entrega (Time-to-Market)? (Então talvez a solução mais "simples" e rápida de implementar, mesmo que "feia", seja a melhor agora.)
- É Desempenho Extremo? (Talvez aquele "Clean Code" com 5 camadas de abstração seja um luxo que não podemos pagar aqui.)
- É Custo Operacional? (Usar aquele serviço gerenciado da AWS vai ser mais caro ou mais barato do que manter um cluster Kubernetes nós mesmos?)
- É Manutenibilidade? (Nosso time é júnior e precisa de um código fácil de entender, mesmo que seja mais lento?)
Só depois de saber o que você está otimizando, você pode analisar o tradeoff de forma justa.
3. Use (e aprenda a gostar dos) ADRs: Architecture Decision Records
Este é o takeaway mais prático que você pode ter. "Arquitetura de Software: As partes difíceis" defende veementemente a documentação das suas decisões.
Um ADR (Architecture Decision Record) é um documento de Markdown simples e curto onde seu time registra uma decisão de arquitetura importante. O formato básico é:
- Título: "Escolha de Comunicação entre Serviço A e B"
- Contexto: "Precisamos que A notifique B quando um pedido for pago."
- Decisão: "Escolhemos usar Comunicação Síncrona (API REST) por enquanto."
- Consequências (O Tradeoff): "Ganhamos simplicidade de implementação e debug. Aceitamos o risco de que, se B falhar, A falhará junto (acoplamento temporal). Isso é aceitável hoje porque..."
- Opções Consideradas (e por que foram descartadas): "Consideramos RabbitMQ, mas o custo de infra e complexidade de monitoramento foi considerado muito alto para a fase atual do projeto."
Um ADR é o antídoto perfeito para a pergunta que todo dev faz 6 meses depois de que algo foi desenvolvido: "Por que diabos nós fizemos isso desse jeito?" Ele força você a articular e lembrar dos porquês dos tradeoffs que você analisou no momento da decisão.
Conclusão: A Habilidade Mais Importante
Por muito tempo, eu interpretava a minha senioridade pela quantidade de "boas práticas" que eu conhecia. Eu achava que o melhor arquiteto era o cara que mais tinha o blueprint da "forma certa" de construir tudo.
Depois de adquirir mais experiência, colecionar algumas cicatrizes de projetos, como a que mencionei no meu artigo sobre overengineering, minha perspectiva mudou completamente. E com certeza o livro “Arquitetura de Software: As partes difíceis” foi uma das lentes que me fez enxergar os porquês de alguns desafios que encontrei (pelo menos uma parcela deles).
A verdadeira senioridade não é sobre ter todas as respostas. É sobre entender que não existem respostas fáceis, apenas tradeoffs difíceis.
A engenharia de software não é uma ciência exata como a matemática; ela é uma disciplina de gerenciamento de complexidade e concessões. O "Mito da Boa Prática" nos dá uma falsa sensação de segurança, enquanto a "Análise de Tradeoffs" nos dá a verdadeira ferramenta para navegar na complexidade do mundo real.
Portanto, da próxima vez que você se encontrar em um debate sobre qual tecnologia é "melhor", pare e mude a pergunta: "Quais são os tradeoffs que estamos fazendo aqui?"
Obrigado pela sua leitura e atenção até aqui. Eu adoraria saber de você, a nossa comunidade é feita de experiências do mundo real.
Qual foi o tradeoff mais difícil (ou doloroso) que você teve que fazer recentemente em um projeto?
Deixe sua história nos comentários!
Top comments (0)