Arquitetura de Software: o que realmente importa (e o que a gente complica demais)
Existe 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. O que mudou não foi a necessidade de arquitetura. O que mudou foi o mundo ao redor 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. Não existia “tech lead”, “staff engineer”, “platform engineer”. Existia a pessoa que entendia o problema e escrevia o código.
Isso não quer dizer que não havia arquitetura. Havia, sim. Mas ela era uma consequência natural do trabalho. O programador que ia construir um sistema de controle de estoque para uma loja pensava na estrutura, definia como os módulos conversavam entre si, decidia o que seria separado do quê. Ele fazia isso porque precisava, não porque alguém criou um papel organizacional para essa função.
O Kent Beck conta que nos primórdios do Extreme Programming, lá no projeto C3 da Chrysler no final dos anos 90, a ideia era justamente essa: o time todo era responsável pela arquitetura. Não tinha um sujeito num pedestal desenhando caixinhas e setas 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. As empresas começaram a contratar 50, 100, 500 desenvolvedores para um único produto. E o que acontece quando você junta 500 pessoas para construir algo? A comunicação vira o gargalo.
O Fred Brooks já tinha avisado lá em 1975, no The Mythical Man-Month: adicionar mais gente a um projeto atrasado só o atrasa mais. E o motivo principal não é falta de competência técnica. É o custo da comunicação. Quando você tem 5 pessoas, existem 10 canais de comunicação possíveis. Com 50, são 1.225. Com 500, passa de 124 mil. A matemática é impiedosa.
Foi nesse contexto que a arquitetura de software ganhou peso como disciplina. Não porque programadores não soubessem organizar código, mas porque alguém precisava organizar pessoas. E aí entra uma verdade que pouca gente fala abertamente: boa parte do que chamamos de “arquitetura de software” é, na prática, um mecanismo para gerenciar a complexidade da comunicação humana.
Pense nos microsserviços, por exemplo. A motivação técnica é real, claro, ter serviços independentes que podem escalar separadamente, ser deployados individualmente, usar tecnologias diferentes. Mas o que realmente impulsionou a adoção massiva de microsserviços nas grandes empresas? 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 a forma como as pessoas se organizavam definia a forma como o software era construído. A arquitetura virou um reflexo do organograma.
O erro brutal da comparação com engenharia civil
Agora, aqui a gente precisa falar de um dos erros mais persistentes do nosso campo: 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”. Parece fazer sentido na superfície. Você tem requisitos, um projeto, uma fase de construção e uma entrega. Mas essa analogia esconde uma diferença fundamental que muda tudo.
Uma ponte, depois de construída, não muda. Ela é projetada para suportar um peso específico, resistir a ventos de determinada velocidade, 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. Que evolui. Um sistema que não pode ser modificado sem dor é um sistema que está morrendo. O Martin Fowler fala bastante sobre isso 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, independente de quão “elegante” ele pareça no diagrama.
E tem aquela ideia, que não lembro exatamente de quem é a autoria original, mas que ressoa com força: software apodrece. Não é uma metáfora exagerada. É o que acontece de verdade. Um sistema que não recebe manutenção, que não é adaptado, que fica parado enquanto o mundo ao redor 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 para a eternidade. A engenharia de software constrói para a mudança. E essa diferença deveria mudar completamente como a gente pensa em arquitetura.
Arquitetura como intenção, não como documentação
Aqui entra o ponto que considero mais importante e mais mal compreendido: o que uma boa arquitetura realmente deveria ser.
Não estou falando daquela arquitetura de 200 páginas de documento, 47 diagramas UML, especificações detalhadas de cada classe e cada método antes de escrever uma linha de código. Esse modelo, o “Big Design Up Front”, já foi extensivamente 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 nenhuma arquitetura, daquela mentalidade de “vamos só codar e ver no que dá”. Isso funciona quando você está sozinho fazendo um projeto pessoal. Num time de 20 pessoas com pressão de negócio, vira caos em semanas.
A boa arquitetura vive num meio-termo que é difícil de definir mas fácil de reconhecer quando você vê. Ela é sobre intenção. Sobre deixar claro qual problema estamos resolvendo, quais são os limites do sistema, onde estão as fronteiras entre responsabilidades, e quais decisões estamos tomando conscientemente versus quais estamos deixando em aberto de propósito.
Um exemplo prático: imagine 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 ser 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.
Percebe a diferença? A arquitetura define as intenções e as restrições importantes. As ferramentas, as linguagens, os detalhes de implementação ficam abertos. Isso dá ao time a flexibilidade de escolher a melhor solução para cada parte, de mudar de ideia quando aprende algo novo, de 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, então isso sim é arquitetura.
A era da IA e por que a intenção importa ainda mais
Agora vamos ao presente, e ao futuro que já está batendo na porta.
Com o avanço das ferramentas de IA para geração de código, a habilidade de escrever código está se tornando menos rara. Ferramentas como o Cursor, o Copilot, o Claude Code, todas elas conseguem gerar código funcional a partir de descrições em linguagem natural. E elas estão melhorando rápido.
Isso muda a equação da arquitetura de uma forma que muita gente ainda não percebeu. Se gerar código ficou mais fácil e mais barato, o que ficou mais valioso? A capacidade de definir o que precisa ser construído. A intenção.
Pense no seguinte cenário: você pede para uma IA gerar um serviço de autenticação. Ela vai gerar algo. 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 decisões por você. E essas decisões podem ser perfeitamente razoáveis para um cenário e completamente inadequadas para o seu.
A arquitetura, nesse contexto, não é sobre controlar como o código é escrito. É sobre definir o espaço de soluções aceitáveis. É sobre dizer “dentro desses limites, qualquer solução que funcione é boa”. Isso vale para humanos e vale para IAs.
E aqui voltamos àquela 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. De 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 é o 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 algo que ficou ainda mais relevante agora: “a função da arquitetura é reduzir a complexidade”. Não é adicionar complexidade com mais camadas, mais padrões, mais abstrações. É reduzir. É tornar o sistema compreensível o suficiente para que qualquer pessoa, humana ou artificial, consiga trabalhar nele sem medo de quebrar tudo.
O contrato invisível: Definition of Ready e Definition of Done
E já que estamos falando de intenção, de clareza, de reduzir ambiguidade sem cair na burocracia, vale fechar com algo que parece simples mas que na prática separa times que funcionam de times que vivem apagando incêndio: Definition of Ready e Definition of Done.
A maioria das pessoas conhece esses conceitos do Scrum e os trata como cerimônia. Mais um checklist para preencher, mais um artefato de processo. Mas quando você olha com cuidado, DoR e DoD são ferramentas de arquitetura disfarçadas de práticas de processo.
Pense na Definition of Ready. O que ela realmente está dizendo? Está dizendo: antes de alguém começar a trabalhar numa tarefa, precisamos ter clareza suficiente sobre o que precisa ser feito. Não clareza absoluta, não uma especificação de 40 páginas. Clareza suficiente. Isso significa 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 no mesmo time interpretarem a mesma história de formas completamente diferentes e só descobrirem isso no review.
Num contexto onde IA está gerando código, a Definition of Ready ganha uma dimensão nova. Porque o que é uma DoR senão 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á essencialmente dizendo: essa tarefa pode ser entregue a qualquer executor, seja um desenvolvedor junior, um sênior ou uma IA, e o resultado vai ser previsível. A qualidade da entrada determina a qualidade da saída. Isso vale para pessoas e vale para modelos de linguagem.
Agora a Definition of Done. Essa é ainda mais arquitetural do que parece. A DoD é o contrato de qualidade do time consigo mesmo. Quando dizemos que algo está “done”, o que isso significa de verdade? O código foi escrito e compila? Tem testes? Foi revisado? Está em produção? A documentação foi atualizada? O monitoramento está configurado?
Cada time vai ter uma DoD diferente, e está tudo bem. O ponto não é ter a DoD perfeita. O ponto é ter uma. Porque sem ela, “pronto” vira uma palavra que cada pessoa 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 pode usar. E aí o que acontece? Retrabalho, frustração, bugs em produção, aquela reunião tensa onde todo mundo aponta o dedo para o outro.
Um exemplo que 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, ninguém sabia por quê, e a correção levava dias. O dia 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 conecta com tudo o que falamos 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 permite o sistema continuar evoluindo. As duas juntas formam um contrato que protege o software contra o apodrecimento que mencionamos antes. Porque software não apodrece só por causa do tempo. Apodrece por causa de decisões acumuladas sem critério, de atalhos que ninguém combinou, de “prontos” que não eram prontos de verdade.
No fundo, DoR e DoD são a arquitetura operacionalizada no dia a dia. São a forma como a intenção arquitetural deixa de ser um documento bonito que ninguém consulta e vira um hábito do time. E hábitos, diferente de documentos, escalam.
O que realmente importa no fim das contas
Se eu tivesse que reduzir tudo isso a uma ideia central, seria esta: arquitetura de software existe para permitir que o software mude.
Não para ficar bonito no diagrama. Não para impressionar em entrevista. Não para criar cargo e título. Existe para que, daqui a dois anos, quando o negócio mudar, quando a tecnologia evoluir, quando um novo requisito aparecer do nada, o sistema consiga se adaptar sem que seja necessário reescrever tudo do zero.
Isso era verdade quando um programador sozinho construía sistemas 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.
A ferramenta muda. A linguagem muda. O paradigma muda. Mas a pergunta fundamental da arquitetura permanece 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 seu papel. Todo 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.