Não há padrão, estilo ou solução “bala de prata”. Toda decisão implica em vantagens e desvantagens marcantes. Priorizar resiliência sacrifica performance. Melhorar a segurança pode comprometer disponibilidade. Combater o acoplamento frequentemente vai na direção contrária do reuso. Não há almoço grátis!
O que entender por asservidade?
Para boa parte dos problemas arquiteturais há pelo menos duas abordagens de solução. Assumir decisões implica, fatalmente, em renúncias. Não raro, por esse motivo, decisões ficam sendo postergadas muito além do último momento responsável.
Cabe aos arquitetos de sotware a responsabilidade de garantir que decisões arquiteturais sejam tomadas no melhor momento. Isso feito, previnindo a ansiedade dos times técnicos e garantindo fluxo de entrega de valor.
Todas as decisões arquiteturais também precisam ser claramente justificadas. Ou seja, é importante que, além de comunicar a decisão arquitetural, também seja compartilhado o racional que levou a essa decisão. Isso será, como já sabemos, de enorme valia mais tarde, quando essas decisões forem questionadas.
Request for Comments (RFC)
Uma maneira efetiva de registrar decisões arquiteturais, como já sabemos, é utilizando ADRs.
Decisões arquiteturais nas quais se entende haver necessidade de deliberação podem ser registradas com o status de RFC, estabelecendo, também, um deadline.
RFCs podem ser mantidas em Issues no Github, por exemplo.
Finalmente, é importante que se busquem, sempre, meios apropriados para comunicar decisões arquiteturais. Mandar um e-mail não basta!
O problema da “arquitetura limpa”
De tempos em tempos surgem “receitas de bolo” para a elaboração de arquiteturas. Elas emergem a despeito de seus proponentes e se estabelecem como o “jeito certo” de organizar componentes, distribuir responsabilidades e estabelecer relações. Já foi assim com a “arquitetura hexagonal”, também foi assim com “microsserviços” e, recentemente, está sendo assim com a “mania” da arquitetura limpa – uma forma de organizar sistemas em camadas propostas por Robert Martin.
Principais desafios para a assertividade
Predileção por modismos
O favoritismo por estilos arquiteturais muda de tempos em tempos. Eventualmente, os “pontos de dor” dos estilos arquiteturais vigentes são mitigados em novas propostas. É o caso, por exemplo, do “desacoplamento extremo” dos microsserviços confrontado com camadas. A tendência ao reuso do passado foi sendo substituída pelo desejo de criar componentes com baixo acoplamento. Tudo é transitório.
Tecnologias que mudam
As verdades de ontem sequer são consideradas hoje. Há relativamente pouco tempo, sequer existiam ferramentas como Kubernetes ou Docker, embora contêineres fossem tecnicamente possível há décadas. Em alguns anos, Docker e Kubernetes também serão tecnologias ultrapassadas.
… rápido
Nos anos recentes, aliás, não só a mudança é uma certeza, mas também está cada vez mais acelerada. O sentimento de “melhor ferramenta/tecnologia/paradigma de todos os tempos da última semana” com frequência causa desgaste sem compensações razoáveis para o negócio.
Escopos (de domínio) cada vez mais complexos
Cada vez mais, se espera software mais onipresente, inteligente e integrado. Aliás, software melhor tem autorizado práticas mais complexas no domínio, que por sua vez tem demandado software ainda melhor, em um ciclo as vezes virtuoso, outras vezes vicioso.
Ambientes (potencialmente) hostis
Em contextos controlados, mudanças nas regulações implicam em desafios para cumprimento de prazos que, frequentemente, ignoram as dificuldades na atualização dos sistemas.
Sistemas dependentes em demasia de componentes externos podem ser fortemente impactos com ajustes de políticas comerciais ou com custos de licenciamento.
Estruturas organizacionais doentes ou inadequadas podem impossibilitar a utilização de estilos arquiteturais como desdobramento da lei de Conway.
Identificando e adotando “abordagens certas”
Arquitetos, como “trabalhadores do conhecimento”, trabalham para “resolver problemas”. Todo sistema a ser desenvolvido pode ser descrito como um conjunto de problemas que esperam soluções.
Saber reconhecer a “natureza” de um problema ajuda a definir a estratégia mais efetiva para o resolver, maximizando o desempenho/resultado e minimizando o esforço/trabalho. Os cinco domínios do framework Cynefin – um framework desenvolvido por David Snowden na IBM – ajudam na classificação de problemas, direcionando abordagem para solução.
A proposta central do framework Cynefin é classificar problemas em cinco domínios diferentes.
O primeiro domínio, “Óbvio” ou “Simples”, é aquele ao qual pertencem os problemas que tem clara relação entre causa e efeito. Sabe-se, com relativa precisão, qual ação precisa ser realizada para atingir um determinado resultado. Problemas desse domínio podem ser corrigidos, evitados ou mitigados através da aplicação de melhores práticas. Além disso, quem os tratará precisa apenas de treinamento e capacidade analítica para detectar, categorizar e aplicar soluções. Raramente será o caso na arquitetura de software. Eventualmente, no desenvolvimento de ferramentas utilitárias.
O segundo domínio, “Complicado”, é aquele ao qual pertencem os problemas que também tem relação clara entre causa e efeito, porém, geralmente, onde diferentes ações podem atingir um mesmo resultado. Problemas desse domínio são, geralmente, melhor resolvidos por pessoas com experiência, que conseguem julgar os prós e contras de diversas alternativas e escolher o que é mais adequado ao contexto. Problemas complicados são comuns no design não arquitetural.
O terceiro domínio, “Complexo”, é aquele onde também é possível estabelecer relação de causa e efeito. Entretanto, apenas depois de aplicada uma ação. Esses são aqueles problemas que precisam de experimentação para serem resolvidos. Afinal, o número de variáveis é tão grande que impossibilita previsão 100% segura. Nesse domínio encontra-se, por exemplo, o desenvolvimento de negócios e a maioria dos desafios arquiteturais.
O quarto domínio, “Caótico”, é aquele onde não é possível determinar, com clareza, relações de causa e efeito, nem antes, nem depois de adotada uma ação. São problemas onde o número de variáveis é muito grande e é impossível de determinar causalidades. Problemas desse domínio costumam ocorrer em situações atípicas, geralmente associadas a circunstâncias críticas. Sistemas distribuídos elaborados de maneira descuidada tendem ao caos. Sistemas caóticos podem ser “influenciados”, mas nunca “determinados”.
Categorias diferentes de problemas exigem abordagens diferentes para determinar uma solução.
Abordagens complicadas não suportam soluções complexas
A “mentalidade binária” empregada para resolver problemas “óbvios” geralmente conduz a paralisia por análise em cenários complexos. Afinal, cenários “óbvios” são resolvidos com conhecimento e “força bruta”, enquanto cenários “complexos” só podem ser resolvidos com “tentativa-aprendizado-e-erro”.
A urgência de práticas ágeis
É a natureza complexa do negócio que é a responsável por não ser possível que metodologias “cascata” funcionem para o desenvolvimento de software. Metodologias ágeis são uma tentativa de facilitar experimentação durante o desenvolvimento de software, sem comprometer eficiência.
Principais aspectos influenciadores
Na prática, as dificuldades das decisões arquiteturais são mitigadas pela redução da quantidade de variáveis (dimensionalidade) que podem influenciar na tomada de decisões arquiteturais. Quanto menos “aspectos discutíveis”, mais delimitadas são as alternativas que precisam ser consideradas e mais assertivas são as decisões.
Sabendo-se, por exemplo, que o objetivo não é “escalar times”, descarta-se a utilização de microsserviços. Quando times não podem ser “tecnicamente” apartados, descarta-se a adoção de modelos simples de camadas. Logo, estrutura organizacional, considerando tamanho e forma de organizar os times são dois aspectos influenciadores fundamentais.
A forma como os dados estão sendo gerenciados na organização também é impactante. Domínios que demandam “dados quentes” para organização tática e estratégica são menos tolerantes para sistemas distribuídos e também tendem a não suportar bem a escala. Dados centralizados se convertem em gargalo. Dados distribuídos oferecem desafios a consistência.
Restrições com fornecedores e regulação invariavelmente restringem opções arquiteturais relevantes. Se há restrições de utilização de nuvem, por exemplo, restringe-se consideravelmente a adoção de componentes prontos.
Finalmente, é importante observar o conhecimento dos times e a familiaridade com naturezas, tanto de problemas, como de designs propostos. Por exemplo, se uma organização não tem maturidade na adoção de práticas ágeis, estilos arquiteturais que dependem dessas práticas irão ter dificuldades de realização.
Isomorfismo entre domínio e arquitetura
Alguns domínios de problema parecem ter match perfeito com alguns estilos arquiteturais. Pipes and Filters, por exemplo, encaixam perfeitamente com domínios organizados em torno de sequências de atividades bem determinadas. Microkernel, outro estilo arquitetural, é perfeito para cenários que demandam customização.
Por outro lado, há domínios de problema que conflitam evidentemente com alguns estilos arquiteturais. Domínios que demandam alto nível de “acoplamento semântico” sofrem em estilos extremamente desacoplados ou distribuídos.
Sobre a necessidade de assumir dívidas técnicas
Dívidas técnicas surgem quando desenvolvedores de software, de forma deliberada ou não, abrem mão de boas práticas de desenvolvimento para ganhar algum tempo. Entretanto, acabam sofrendo, mais tarde, com os custos e tempos de manutenção mais altos. Sob a perspectiva de arquitetura de software, elas acontecem quando decisões de design não ótimas realizadas.
Os “juros” resultantes das dívidas técnicas, para programadores, costumam ficar evidentes em maior dificuldade para ler e entender o código, fragilidade decorrente de acoplamento (mexer em uma parte do sistema, estraga outra), dificuldade para adicionar features sem quebrar testes, etc. Para a arquitetura, os “juros” ficam evidentes pela incapacidade de atingir os objetivos do negócio, respeitar restrições ou atender atributos de qualidade.
É sempre importante ressaltar que, se não há “juro” percebido, provavelmente não há dívida. Ou seja, se nenhum prejuízo é sentido pelo time técnico, ou pelo negócio, por causa de uma decisão técnica que aparentava não ser boa, então, talvez não haja nada de errado com ela!
Não é incomum encontrar técnicos implementando soluções mais “sofisticadas” justificando-as por pretensas boas práticas, mas que, na verdade, adicionam pouco ou nenhum valor ao negócio. Aliás, com frequência adicionam apenas complexidade e, consequentemente, custo.
Se um desenvolvedor ou arquiteto adota uma “boa prática” sem saber justificar a razão para estar fazendo isso, então, há chances grandes do efeito ser exatamente o oposto do que planeja. Microsserviços implementados para viabilizar escalabilidade, mas que não escalam. Repositórios isolando acesso a dados para permitir uma hipotética “troca do banco”, que nunca ocorre.
As “sementes” das dívidas técnicas arquiteturais
Dívidas técnicas arquiteturais nascem, frequentemente, da necessidade de atender alguma especificidade do negócio. Geralmente, sob uma das seguintes categorias:
- custo;
- time-to-market;
- satisfação dos usuários;
- posicionamento estratégico.
// TODO
Antes de avançar, convido-o as seguintes reflexões:
- As soluções arquiteturais que seu time estão adotando são as melhores, sob o ponto de vista de custo e risco?
- Quais são os principais desafios que tens enfrentado para a assertividade nas decisões arquiteturais?
- Conforme o Cynefin, qual é a categoria dos problemas que tens enfrentado com mais frequência?
- Trabalhou em algum projeto com isomorfismo entre arquitetura e domínio?
- Quais são as principais dívidas técnicas que tens enfrentado em seu projeto recentemente? Elas são arquiteturais?