Quando uma abstração está exercendo seu papel, seu código tende a cada vez parecer mais simples. Ao olhar desatento pode parecer até simplista. O problema é que essa simplicidade ao mesmo tempo que aumenta a produtividade, produz ansiedade e afeta quem depende de complexidade para programar.
No post anterior, Abstrações e seu ciclo evolutivo, eu abordei uma ideia que até cheguei a colocar em prática, mas não tive tempo usando-a para tirar conclusões mais elaboradas sobre o que acontece no dia-a-dia. Mas dessa vez eu vou falar sobre algo que tive de lidar em ~2013.
Qual era o problema?
Eu havia trazido o Oragon para dentro do projeto principal que estava construindo e que chamei de iMusica Enterprise. Diferente da proposta do que defendo em Abstrações e seu ciclo evolutivo, nesse projeto eu havia um fork e trouxe para dentro da iMusica. Vale lembrar que o Oragon é meu, naquela época era resultado de mais de 10 criando abstrações e mais abstrações que facilitavam o dia-a-dia. Mas, como aquilo era novo para aquele time, eu resolvi trazer o código como meio de tornar esse mindset de abstrair a complexidade algo cotidiano.
Ahhh como eu era inocente!
Eu estava inspirado a criar um novo monolito, dessa vez bem modelado e consistente. Um passo prévio para a identificação da delimitação dos contextos, visando mais à frente em alguns anos, quebrar o projeto em coisas menores. Não estava claro se seria microsserviço ou não, mas na prática eu nem entendia o que microsserviço era de fato.
Aliás, em MonolithFirst @ Fowler, artigo comentado de 2016, eu encontro amparo para a minha própria intuição que apontava que não tinha tinha consciência e maturidade para decidir sobre onde quebrar o projeto, por conta de não conhecer o domínio o suficiente para isso. E por isso criar um monolito desenhado para ser quebrado posteriormente.
De qualquer forma o que estava claro era que merecia, futuramente, quebrar o projeto. Mas nem o grafo de dependências estava muito claro para ninguém que estava lá há anos, muito menos para mim com meses.
O que dava para enxergar desde os primeiros dias, era o óbvio:
Precisava de uma nova base de código que fosse modelada “da forma certa”, já que até então o que existia era uma colcha de retalhos em que se buscou fazer da forma que dava, em vez de fazer o que precisava ser feito. Um arroz com feijão bem feito, só!
Incorporar não foi suficiente
Eu achava que trazendo o código do Oragon para esse projeto, eu estaria facilitando o dia-a-dia do time, porque na minha cabeça, uma vez que o código estivesse embarcado na mesma solution, magicamente se alguém quisesse entender e estudá-lo, seria prático de ler, prático de debugar, prático de entender o fluxo. Quem não quisesse, ok, você tinha exemplos suficientes de como usar. Então você poderia ser usuário da arquitetura e se quisesse, poderia ser colaborador também! Eu estava completamente equivocado. Lembro de apenas 2 ou 3 pessoas terem algum interesse em como as coisas funcionavam.
Mas uma terceira pessoa queria a todo custo participar daquela complexidade. Queria construir uma nova abstração. Mas o problema era que não havia fundamento algum, exatamente porque não só havia uma abstração para aquela finalidade, como a tal abstração estava com um bom nível de maturidade, resistindo intacta à novas demandas, resistindo aos novos requisitos que chegaram nos meses anteriores.
E aí me veio à cabeça algumas questões:
- Estava claro que o motivador era o interesse em fazer algo complexo e desafiador
- Isso é natural de um problem solver
- Isso é perfeitamente aceitável e esperado de um bom dev
- Mas criaria um problema para a arquitetura
- Não havia um bom motivo para a criação daquela nova abstração
- Outra abstração, madura, pré-existente deveria ser usada ou refatorada para atender á nova demanda/requisito.
- Eu, como líder e gestor precisava criar uma regra que ao mesmo tempo:
- Permitisse que qualquer um criasse novas abstrações na medida em que fossem necessárias
- Mas quando uma nova abstração esbarrasse no escopo da anterior, seria necessário garantir:
- Que a anterior fosse substituída pela nova
- Que não gerasse quebras para quem já dependia daquela abstração
- De forma a encorajar quem sabe o que quer e sabe o que está fazendo
- E ao mesmo tempo desencorajar a criação de abstrações duplicadas, ou com perda de features.
Se eu pudesse resumir, a intenção era garantir que cada um soubesse o que precisa ser feito e o que é considerado correto. Se uma abstração que está funcionando precisa ser refatorada, ela será. Não importa se ela é usada em 1 lugar ou em 100. Sem mimimi, sem ressentimento. Vamos alterar coisas que estão funcionando e partes do sistema que já estavam testadas para garantir que a base de código não fosse corrompida com abstrações dúbias, que confundem mais do que resolvem problemas.
Eu chamo isso de:
Fazer o que precisa ser feito.
Em um projeto em que toda ou pelo menos a maior parte da complexidade arquitetural é delegada à mecanismos e abstrações, o código de negócio parece algo ridiculamente simples e muitas até parece burro. Embora a frase da imagem abaixo se faça presente:
Como lidar com a necessidade de fazer coisas mais complexas e de fato entregar desafios para serem resolvidos?
Minha resposta para essa pergunta é simples. Nesse período eu criei esse conjunto de regras, que desde então regem essa forma de encarar as abstrações:
- Toda complexidade deve ser abstraída em projeto(s) de arquitetura/infraestrutura.
- Sendo totalmente genéricas
- Sem nenhuma dependência direta com o projeto
- Nascendo lado-a-lado com o projeto, podendo estar na mesma solution, como meio de facilitar e promoter o refactoring, até alcançar um nível de maturidade para que seja promovida/isolada.
- Nunca 2 abstrações para a mesma finalidade podem coexistir no mesmo projeto.
- Se há questões com a implementação atual, priorize corrigir ou evoluir a abstração atual.
- Se for o caso de criar uma nova abstração, o item 5 deve ser levado em conta, portanto é escopo obrigatório da nova abstração, substituir a anterior, isso implica em refatorar se necessário, as partes que dependiam da abstração anterior.
Esse conjunto de 7 itens trazem alguns benefícios:
- As abstrações são evoluídas ao invés de duplicadas.
- Para recriar uma abstração, é preciso entender primeiro a versão anterior em sua plenitude.
- Ao refatorar ou recriar a abstração, toda a base de código é atualizada para usar a nova versão.
- Não há meios de se produzir duplicidade de abstrações.
- O ímpeto de buscar problemas para resolver continua sendo endereçado, mas de forma coordenada ao invés de aleatório. Todos se beneficiam daquela implementação.
- A separação clara de responsabilidades demanda uma modelagem mais eficiente, que gera a capacidade da abstração ser promovida de local para regional e posteriormente para global.
- A busca por complexidade não fundamentada é desencorajada.
- A busca por trazer complexidade para o código de negócio é desencorajada.
O impacto dessa decisão:
- Ter de pensar em todos o cenários de uma abstração, é de alguma forma, burocratizar o processo.
- O medo de refatorar aquilo que não foi feito pelo própria desenvolvedor gera desconforto.
- A vontade de sair fazendo, de qualquer jeito, continua sendo um problema, como se fosse um vício. Isso produz negação à essa estratégia.
- Modelar mecanismos que não dependam diretamente do core da aplicação é um desafio, pois por mais que se fale muito de SOLID, na prática SOLID é muito menos usado do que deveria.
0 comentários