Arquitetura de Software: o que realmente importa (e o que a gente complica demais)
Tem uma confusão silenciosa que persiste há anos na nossa área. A gente fala de arquitetura de software como se fosse uma disciplina separada da programação, quase uma arte superior, quando na verdade ela sempre esteve ali, embutida no ato de programar. A necessidade de arquitetura continua a mesma, o que mudou foi o mundo em volta dela.
Quando o programador era tudo
Se você começou a trabalhar com software há mais de 25 anos, ou pelo menos estudou como as coisas funcionavam naquela época, sabe que a figura central era o programador. Não existia “arquiteto de software” como cargo, nem “tech lead”, “staff engineer” ou “platform engineer”. Existia a pessoa que entendia o problema e escrevia o código.
Isso não quer dizer que não tinha arquitetura. Tinha, sim, mas ela era uma consequência natural do trabalho. O programador que ia construir um sistema de controle de estoque pra uma loja pensava na estrutura, definia como os módulos conversavam entre si, decidia o que ficava separado do quê. Ele fazia isso porque precisava, e não porque alguém tinha criado um papel organizacional pra essa função.
O Kent Beck conta que, nos primórdios do Extreme Programming, lá no projeto C3 da Chrysler no fim dos anos 90, a ideia era justamente essa: o time todo era responsável pela arquitetura. Não tinha um sujeito num pedestal desenhando caixinha e seta enquanto o resto só executava. Todo mundo pensava, todo mundo codava, todo mundo decidia junto. E funcionava.
A industrialização que complicou tudo
Aí o software virou indústria de verdade. As equipes cresceram, os projetos ficaram maiores e as empresas começaram a contratar 50, 100, 500 desenvolvedores pra um único produto. Quando você junta 500 pessoas pra construir alguma coisa, a comunicação vira o gargalo.
O Fred Brooks já tinha avisado em 1975, no The Mythical Man-Month, que colocar mais gente num projeto atrasado só atrasa mais. E o motivo principal tem pouco a ver com falta de competência técnica, tem a ver com o custo da comunicação. Com 5 pessoas, existem 10 canais de comunicação possíveis. Com 50, são 1.225. Com 500, passa de 124 mil. Contra essa conta não tem muito o que fazer.
Foi nesse contexto que a arquitetura de software ganhou peso como disciplina. Não porque programador não soubesse organizar código, mas porque alguém precisava organizar pessoas. E tem uma verdade aí que pouca gente fala abertamente: boa parte do que a gente chama de “arquitetura de software” é, na prática, um mecanismo pra gerenciar a complexidade da comunicação humana.
Pensa nos microsserviços, por exemplo. A motivação técnica é real, claro: ter serviços independentes que escalam separadamente, que podem ser deployados individualmente, que podem usar tecnologias diferentes. Mas pra mim o que realmente puxou a adoção em massa de microsserviços nas grandes empresas foi a Lei de Conway, a ideia de que a estrutura do software vai espelhar a estrutura de comunicação da organização. O Spotify não criou squads e tribes por acidente. Eles perceberam que o jeito como as pessoas se organizavam definia o jeito como o software era construído. A arquitetura virou um reflexo do organograma.
O erro de comparar software com engenharia civil
Aqui eu preciso falar de um dos erros mais persistentes do nosso campo, que é a comparação entre software e construção civil.
Todo mundo já ouviu alguma variação de “construir software é como construir uma ponte” ou “precisamos de engenharia de verdade, como na construção de prédios”. Na superfície parece fazer sentido. Você tem requisitos, um projeto, uma fase de construção e uma entrega. Mas essa analogia esconde uma diferença que, pra mim, pesa mais do que todas as semelhanças.
Uma ponte, depois de construída, não muda. Ela é projetada pra aguentar um peso específico, resistir a vento de determinada velocidade e durar 100 anos fazendo exatamente a mesma coisa. Se alguém chegar e disser “agora essa ponte precisa ser 30% mais larga e suportar trens além de carros”, a resposta é simples: você constrói outra ponte.
Software não funciona assim. Software bom é aquele que muda, que se adapta e evolui. Um sistema que não pode ser modificado sem dor é um sistema que está morrendo. O Martin Fowler fala bastante disso quando discute design evolutivo. O Uncle Bob, o Robert C. Martin, vai ainda mais longe e defende que a principal métrica de um bom design é o custo da mudança. Se mudar é barato, o design é bom. Se mudar é caro e arriscado, o design falhou, por mais “elegante” que ele pareça no diagrama.
E tem aquela ideia, que eu não lembro exatamente de quem é a autoria original, mas que pra mim faz muito sentido: software apodrece. E a metáfora não tem exagero nenhum. Um sistema que não recebe manutenção, que não é adaptado, que fica parado enquanto o mundo em volta muda, começa a apodrecer. As dependências ficam desatualizadas. As abstrações que faziam sentido três anos atrás agora atrapalham. As integrações com sistemas externos quebram porque a API do outro lado mudou. O software que parecia sólido vira uma carga, um peso, um legado no pior sentido da palavra.
A engenharia civil constrói pra eternidade, a engenharia de software constrói pra mudança. E essa diferença deveria mudar completamente o jeito como a gente pensa arquitetura.
Arquitetura como intenção, não como documentação
Aqui entra o ponto que eu considero mais importante e mais mal compreendido: o que uma boa arquitetura deveria ser de verdade.
Não estou falando daquela arquitetura de 200 páginas de documento, 47 diagramas UML e especificação detalhada de cada classe e cada método antes de escrever uma linha de código. Esse modelo, o “Big Design Up Front”, já foi muito criticado, e na prática todo mundo que já trabalhou num projeto assim sabe que metade daqueles documentos fica desatualizada antes do primeiro sprint terminar.
Mas também não estou falando de arquitetura nenhuma, daquela mentalidade de “vamos só codar e ver no que dá”. Isso funciona quando você está sozinho num projeto pessoal. Num time de 20 pessoas com pressão de negócio, vira caos em semanas.
A boa arquitetura fica num meio-termo difícil de definir, mas fácil de reconhecer quando você vê. Ela é sobre intenção, sobre deixar claro qual problema a gente está resolvendo, quais são os limites do sistema, onde ficam as fronteiras entre responsabilidades, e quais decisões estamos tomando conscientemente e quais estamos deixando em aberto de propósito.
Um exemplo prático: imagina que você está construindo um sistema de pagamentos. Uma boa arquitetura não vai especificar se você deve usar PostgreSQL ou MongoDB, se o serviço de notificação deve ser escrito em Go ou Python, ou se a fila de mensagens deve ser RabbitMQ ou Kafka. Ela vai dizer coisas como: o processamento de pagamento é o core do sistema e precisa ficar isolado, com contratos claros de entrada e saída. A notificação ao cliente é um efeito colateral que não pode bloquear o fluxo principal. A conciliação financeira precisa funcionar de forma idempotente porque vai rodar em batch e pode ser reexecutada.
Repara que a arquitetura define as intenções e as restrições importantes e deixa abertos as ferramentas, as linguagens e os detalhes de implementação. Isso dá ao time flexibilidade pra escolher a melhor solução pra cada parte, pra mudar de ideia quando aprende algo novo, pra adaptar o sistema sem ter que jogar tudo fora.
O Simon Brown, criador do modelo C4 de documentação de arquitetura, tem uma frase que resume bem: “arquitetura é sobre as decisões significativas, onde significativo é medido pelo custo da mudança”. Se trocar o banco de dados é fácil porque você isolou bem a camada de persistência, então a escolha do banco não é uma decisão arquitetural. Se trocar o modelo de comunicação entre serviços implica reescrever metade do sistema, aí sim é arquitetura.
A era da IA e por que a intenção importa ainda mais
Agora o presente, e o futuro que já está batendo na porta.
Com o avanço das ferramentas de IA pra geração de código, saber escrever código está ficando menos raro. Cursor, Copilot, Claude Code, todas essas ferramentas conseguem gerar código funcional a partir de descrição em linguagem natural, e estão melhorando rápido.
Isso mexe com a equação da arquitetura de um jeito que muita gente ainda não percebeu. Se gerar código ficou mais fácil e mais barato, o que passa a valer mais é a capacidade de definir o que precisa ser construído, ou seja, a intenção.
Pensa no seguinte cenário: você pede pra uma IA gerar um serviço de autenticação. Ela vai gerar alguma coisa, e provavelmente vai funcionar. Mas se você não especificou que precisa suportar múltiplos provedores de identidade, que os tokens precisam ter um ciclo de vida específico, que o sistema precisa funcionar em múltiplas regiões com latência aceitável, a IA vai tomar essas decisões por você. E essas decisões podem ser perfeitamente razoáveis pra um cenário e completamente inadequadas pro seu.
Nesse contexto, arquitetura tem menos a ver com controlar como o código é escrito e mais a ver com definir o espaço de soluções aceitáveis. É dizer “dentro desses limites, qualquer solução que funcione é boa”. Isso vale pra humanos e vale pra IAs.
E aqui eu volto naquela questão da especificação. Não a especificação pesada, com centenas de documentos que ninguém lê, mas a especificação como ato de pensar com clareza. Escrever, em poucas palavras, o que o sistema precisa fazer, por que precisa fazer e quais são as restrições reais (não as imaginárias). É isso que separa um prompt que gera código útil de um prompt que gera código que você vai jogar fora em duas semanas.
O Grady Booch, um dos pais da UML e da orientação a objetos, disse uma coisa que ficou ainda mais relevante agora: “a função da arquitetura é reduzir a complexidade”. Reduzir, e não sair empilhando mais camada, mais padrão, mais abstração. Deixar o sistema compreensível o bastante pra que qualquer um, humano ou artificial, consiga trabalhar nele sem medo de quebrar tudo.
O contrato invisível: Definition of Ready e Definition of Done
E já que estou falando de intenção, de clareza, de reduzir ambiguidade sem cair na burocracia, quero fechar com uma coisa que parece simples, mas que na prática separa os times que funcionam dos times que vivem apagando incêndio: Definition of Ready e Definition of Done.
A maioria das pessoas conhece esses conceitos do Scrum e trata como cerimônia, mais um checklist pra preencher, mais um artefato de processo. Mas quando você olha com cuidado, DoR e DoD são ferramentas de arquitetura disfarçadas de prática de processo.
Pensa na Definition of Ready. O que ela diz é que, antes de alguém começar a trabalhar numa tarefa, a gente precisa ter clareza suficiente sobre o que precisa ser feito. Não clareza absoluta, nem uma especificação de 40 páginas. Clareza suficiente. Isso quer dizer que o problema está entendido, que as dependências foram identificadas, que os critérios de aceitação existem e fazem sentido. É um filtro contra a ambiguidade destrutiva, aquela que faz três pessoas do mesmo time interpretarem a mesma história de jeitos completamente diferentes e só descobrirem isso no review.
Num contexto em que a IA está gerando código, a Definition of Ready ganha uma dimensão nova, porque uma DoR é basicamente um prompt bem escrito. Quando você define que uma tarefa só está “ready” quando tem contexto claro, critérios de aceitação definidos e restrições explícitas, você está dizendo que essa tarefa pode ser entregue a qualquer executor, seja um desenvolvedor júnior, um sênior ou uma IA, e o resultado vai ser previsível. A qualidade da entrada determina a qualidade da saída, e isso vale pra pessoas e pra modelos de linguagem.
Agora a Definition of Done, que é ainda mais arquitetural do que parece. A DoD é o contrato de qualidade do time com ele mesmo. Quando a gente diz que algo está “done”, o que isso quer dizer de verdade? O código foi escrito e compila? Tem teste? Foi revisado? Está em produção? A documentação foi atualizada? O monitoramento está configurado?
Cada time vai ter uma DoD diferente, e tudo bem. Ninguém precisa da DoD perfeita, precisa ter uma. Sem ela, “pronto” vira uma palavra que cada um interpreta do jeito que convém. O desenvolvedor acha que está pronto quando o código funciona na máquina dele. O QA acha que está pronto quando passa nos testes. O product owner acha que está pronto quando o cliente consegue usar. Aí vem retrabalho, frustração, bug em produção e aquela reunião tensa em que todo mundo aponta o dedo pro outro.
Um exemplo que eu vivi mais de uma vez: um time sem DoD clara entregava funcionalidades que “funcionavam”, mas sem testes automatizados, sem logging adequado, sem métricas de observabilidade. Em staging tudo parecia lindo. Em produção, quando algo quebrava, ninguém sabia onde nem por quê, e a correção levava dias. No dia em que o time decidiu que “done” incluía testes de integração, logs estruturados e pelo menos um alerta básico de saúde, a velocidade de entrega pareceu cair no primeiro mês. Mas em três meses a velocidade real, a de entregar valor que fica de pé, tinha dobrado.
E isso conversa com tudo o que eu falei até aqui. A DoR é arquitetura na entrada, garante que a intenção está clara antes de começar. A DoD é arquitetura na saída, garante que o resultado atende ao padrão que deixa o sistema continuar evoluindo. As duas juntas formam um contrato que protege o software do apodrecimento de que eu falei antes. Software não apodrece só por causa do tempo, apodrece também por decisão acumulada sem critério, por atalho que ninguém combinou, por “pronto” que não estava pronto de verdade.
Na prática, DoR e DoD são a arquitetura funcionando no dia a dia. É por meio delas que a intenção arquitetural deixa de ser um documento bonito que ninguém consulta e vira hábito do time. E hábito, diferente de documento, escala.
O que importa no fim das contas
Se eu tivesse que reduzir tudo isso a uma ideia central, seria esta: arquitetura de software existe pra permitir que o software mude.
Ela não existe pra ficar bonita no diagrama, pra impressionar em entrevista ou pra criar cargo e título. Existe pra que, daqui a dois anos, quando o negócio mudar, quando a tecnologia evoluir, quando um requisito novo aparecer do nada, o sistema consiga se adaptar sem precisar ser reescrito do zero.
Isso era verdade quando um programador sozinho construía sistema em Delphi nos anos 90. É verdade hoje, com times de centenas de pessoas usando microsserviços na nuvem. E vai continuar sendo verdade quando a maior parte do código for gerada por IA e o papel do humano for definir a intenção, a direção e os limites.
Ferramenta, linguagem e paradigma mudam. A pergunta de base da arquitetura continua a mesma: como a gente organiza isso de um jeito que permita mudar amanhã sem sofrer?
Se a sua arquitetura responde bem a essa pergunta, ela está cumprindo o papel dela. O resto é detalhe.
Referências
Beck, K. (1999). Extreme Programming Explained: Embrace Change. Addison-Wesley. O relato do projeto C3 da Chrysler e a filosofia de que a responsabilidade pela arquitetura é do time inteiro, não de um indivíduo isolado.
Brooks, F. P. (1975). The Mythical Man-Month: Essays on Software Engineering. Addison-Wesley. O clássico que formalizou a ideia de que adicionar pessoas a um projeto atrasado o atrasa mais, e a análise do custo exponencial da comunicação em equipes grandes.
Conway, M. E. (1968). “How Do Committees Invent?” Datamation, 14(4), 28–31. O artigo original que deu origem à Lei de Conway, argumentando que a estrutura de um sistema reflete a estrutura de comunicação da organização que o construiu.
Fowler, M. (2004). “Is Design Dead?” Publicado originalmente em martinfowler.com. Uma reflexão sobre design evolutivo versus Big Design Up Front, e como métodos ágeis mudaram a relação entre design e implementação. Disponível em: https://martinfowler.com/articles/designDead.html
Martin, R. C. (2017). Clean Architecture: A Craftsman’s Guide to Software Structure and Design. Prentice Hall. A defesa de que a qualidade de uma arquitetura se mede pelo custo da mudança, e que boas abstrações protegem o software contra o envelhecimento.
Brown, S. (2018). The C4 Model for Visualising Software Architecture. Disponível em: https://c4model.com. O modelo que propõe documentar arquitetura em níveis de abstração (contexto, container, componente, código) e a ideia de que decisões arquiteturais são aquelas cujo custo de reversão é alto.
Booch, G. (2006). “On Architecture.” Blog da IBM. A reflexão sobre o papel da arquitetura como mecanismo de redução de complexidade, não de adição.
Foote, B. & Yoder, J. (1997). “Big Ball of Mud.” Apresentado na Fourth Conference on Patterns Languages of Programs (PLoP ‘97). O paper que descreve como sistemas de software tendem a degradar em direção a uma massa amorfa e sem estrutura quando não há cuidado arquitetural contínuo. Uma das referências mais citadas sobre o conceito de “software rot”. Disponível em: http://www.laputan.org/mud/
Kniberg, H. & Ivarsson, A. (2012). “Scaling Agile @ Spotify with Tribes, Squads, Chapters & Guilds.” O whitepaper que documentou o modelo organizacional do Spotify e como a estrutura de times influenciava diretamente a arquitetura do software produzido.
Schwaber, K. & Sutherland, J. (2020). The Scrum Guide. Disponível em: https://scrumguides.org. A definição oficial de Definition of Done como compromisso de qualidade do time, e o framework que popularizou os conceitos de DoR e DoD como práticas de gestão de trabalho.
Hunt, A. & Thomas, D. (1999). The Pragmatic Programmer: From Journeyman to Master. Addison-Wesley. Introduz o conceito de “software entropy” e a metáfora das janelas quebradas aplicada ao código, argumentando que pequenas degradações ignoradas aceleram o apodrecimento do sistema como um todo.