Apodrecendo seu software com rebeldia

É muito raro toparmos com sistemas que foram feitos para serem efêmeros. Então por que é tão comum vermos projetos que nascem para ser eternos e rapidamente apodrecem diante dos olhos de seus stakeholders?

Iniciei uma pesquisa interna na itexto em que elencamos quais os fatores que levam um sistema a deteriorar. No topo está um comportamento que, neste post, chamarei carinhosamente de "rebeldia".

Uma história em três atos

Todo software tem uma história e desconfio que há um número bastante reduzido de variações no que diz respeito a este assunto. Segue uma bem comum...

Primeiro ato: a felicidade do ambiente familiar

Projeta-se o sistema de tal modo que se conhece todos os seus elementos. A arquitetura é clara: sabe-se quais tecnologias e ferramentas deverão ser adotadas e por que se encaixam bem neste cenário. Os artificies dominam suas ferramentas (ou ao menos gostam delas) e esculpem com primor seu sistema.

Não raro é neste momento em que a maior parte dos testes são escritos, que a documentação essencial é gerada e mantida. O primeiro release talvez seja um pouco problemático, mas a partir do segundo (release) os problemas originais já estão dominados (ou ao menos conhecidos) e observa-se sucessivas implantações bem sucedidas.

A família é unida no objetivo de desenvolver aquele sistema da melhor forma possível com as restrições adotadas em sua concepção. Restrições estas consistindo nas ferramentas de desenvolvimento adotadas, frameworks, padrões arquiteturais e convenções que, mesmo não sendo bem documentadas (se documentadas), fazem parte do dia a dia daquele meio familiar.

Segundo ato: a família (e o mundo) muda

A estrutura familiar nunca é eterna: membros saem e entram na equipe, os participantes originais se afastam com o tempo. E o até então amado príncipe, antes protegido em seu lar doméstico acaba tendo seu primeiro contato com o mundo externo.

Novos membros, novas ideias (o que é sempre bom!). E as pessoas observam nosso príncipe e questionam sua natureza. Será que realmente deveria ser desta maneira? Por que é assim?

Terceiro ato: rebeldia e morte

Os novos amigos de nosso herói começam a questionar sua natureza. "Ainn... você é feito em Java??? Mas Python é tão mais legal... Coisas 'data science' são feitas em Python.... Coisas fashion são feitas em Node... PHP??? Uck, que NOXIO!".

Nosso herói que antes era tão belo de repente parece enfeiado, fora de moda, ultrapassado. Mas nem tudo está perdido: sua nova família e amigos estão aqui para lhe rejuvenescer. Basta que mude sua natureza, ou seja, sua arquitetura, frameworks, ferramentas de desenvolvimento... Resumindo: reescreve este negócio!

Mas a reescrita é inviável...

Resolve-se o problema: "por que não usar o que temos, mas de outro modo?"

Subverte-se o uso do que existe. Tudo bem: foi feito no framework X, mas podemos escrever novas funcionalidades neste mesmo framework X como se fosse Y! Não, melhor ainda: por que não usar um paradigma funcional nesta linguagem que não tem suporte para tal? Espera aí: por que apenas uma base de dados relacional??? Vamos colocar um NoSQL aqui no meio também para termos maior escalabilidade!

Nem todos os amigos pensam assim: há também os depressivos. Estes desenvolvem novas funcionalidades com o que há, mas apenas com os conhecimentos que já tem. Pra que me aprofundar nestas tecnologias que não são usadas lá fora? Eu mesmo to saindo daqui em breve... Que se foda esta merda, vou escrever como sei só pra entregar e que se danem estes miseráveis que criaram esta monstruosidade....

A deterioração se mostra aos olhos de todos. Ela chega de mansinho e vai crescendo a cada dia... Alguns amigos deixam a cidade, outros entram para a turma, mais novas ideias, mais soluções...

Nosso herói deteriora-se ainda mais. A família original não mais está presente, apenas novos amigos, que observam sua cada vez mais rápida decadência.

E nosso herói morre. O custo de mantê-lo é maior que o de sua reescrita. E se os stakeholders tiverem sorte (dinheiro), poderão ao menos tentar recomeçar novamente (e o ciclo se repete).

Já viu esta história? Eu sim: várias vezes. Agora vamos fechar o livro e ir pra prática.

Sempre há um framework

Algum tempo atrás neste blog expus minha definição do que seria um framework sob um ponto de vista totalmente voltado para a construção de software (clique aqui). Hoje vou ampliar este conceito. Como eu definiria um framework hoje? Neste primeiro momento, o definira como...

O framework são as regras do jogo.

Weissmann, Henrique Lobo

Tudo o que disse naquele post se mantém, só que agora me refiro a um contexto bem mais amplo. O framework define, por exemplo, qual a arquitetura daquele sistema. Imaginemos um sistema web simples, composto por duas unidades: seu front-end escrito em Angular e seu back-end, que fornece uma API consumida pelo primeiro. Há um framework aqui no sentido de que há regras a serem seguidas que são essenciais:

  • O protocolo de comunicação será o HTTP(S) ou Websocket.
  • As páginas serão renderizadas como HTML.
  • Front-end não define as regras profundas de negócio.
  • Eu sei quais os endpoints disponibilizados pelo meu back-end.
  • Meu back-end pode ser implementado em qualquer coisa que propicie disponibilizar interações usando HTTP ou Websocket.

É claramente um framework aqui: as regras são claras, então você sabe como interagir com o sistema. Todos sabem aonde as peças (e quais peças) devem se encaixar.

O framework também define como a equipe interage. Sistema não é só código, é também interação entre os membros da equipe. Vamos a algumas regras?

  • Aplicação ou não de determinada metodologia ou prática (TDD, XP, Scrum, etc).
  • Definição de procedimentos de gestão de configuração e mudança.
  • Procedimentos de validação de entregáveis.
  • Como é gerido o projeto no que diz respeito às atividades e papéis dos membros da equipe.

Recentemente presenciamos um caso de rebeldia bastante interessante. Causou nos stakeholders uma excelente primeira impressão que em pouco tempo se mostrou uma sensação bastante amarga.

Um desenvolvedor front-end queria gerar um relatório no formato PDF (algo factível de ser feito usando apenas recursos do navegador hoje em dia). Ele conhecia JavaScript, sabia que com Node seria possível fazer isto usando bibliotecas nativas. Mas não queria incluir estas bibliotecas no front-end. Qual a solução? Simples: implementou uma API secreta em Node.js que gerava o arquivo. Brilhante solução em um primeiro momento, certo? O primeiro momento foi de sucesso. O que pode dar errado?

Muita coisa: com o tempo gerou-se um conjunto de APIs "secretas" no sistema. Endpoints sem qualquer documentação que, posteriormente, descobriu-se inclusive implementar algumas regras de negócio profundas que precisaram ser depois transferidas para a API principal. Um pequeno ato de rebeldia que apresenta falsa beleza e acabou por ruir o sistema a médio prazo.

Frameworks são burocráticos no sentido que definem os padrões a serem seguidos. A palavra burocracia soa mal para muitos, mas na prática é essencial para se ter ordem.

Quando a rebeldia é válida?

Lendo este post você deve estar me achando a pessoa mais inflexível do mundo, né? Garanto que não é o caso. Acredito (e incentivo) atos de rebeldia na itexto o tempo inteiro, mas há uma diferença: pra ser rebelde, é importante saber contra o que se rebeldia.

O grande problema é que na esmagadora maioria dos atos de rebeldia que encontro não há uma análise profunda a respeito do framework corrente. As principais causas de "rebeldia" que encontro não tem nada a ver na realidade. Segue uma lista:

  • A pessoa gostaria de trabalhar com outra tecnologia.
  • A pessoa acha aquele conjunto de tecnologias antiquado.
  • A pessoa é preguiçosa e não quis aprender o negócio por que pretende sair logo da equipe.
  • "Por que sim" (tem muita diva por aí).
  • Você se torna acidentalmente um rebelde: simplesmente não tem ânimo com seu trabalho atual. É triste e inconsequente, mas acontece.
  • Por que você pode lucrar horrores sendo desonesto e dizendo que a reescrita é o caminho mais interessante (raríssimas vezes vi o caso que fosse).

Não, se é pra se rebelar, que seja um motim bem fundamentado. Se é pra trocar, por exemplo, o framework web, precisamos de excelentes razões para tal (por que isto requer normalmente a reescrita de boa parte do sistema). As seguintes perguntas precisam ser respondidas:

  • Por que mudar? (repita esta pergunta ao menos umas três vezes pra pessoa)
  • O que ganhamos com isto?
  • O que o stakeholder ganha com isto?
  • Trocar pelo que?
  • Quais os riscos e o custo desta troca?
  • Os ganhos justificam os riscos?
  • Outra pessoa além de você vai conseguir dar manutenção nisto?
  • A alternativa é efêmera?
  • E por aí vai...

Rebelde sem causa só é muito legal no cinema. Se você é desenvolvedor e um rebelde sem causa nestes aspectos, vou te contar: você é irresponsável (um moleque), por que não sofrerá consequências tão significativas quanto os stakeholders do projeto.

Rebeldia só se justifica quando os ganhos justificam os riscos. De nada adianta "inovar" se o destino é o brejo.

Há solução para sistemas "pós rebelião"?

Sim, é bem simples e temos diversos casos de sucesso na itexto nestes contextos. Qual a solução? Vamos lá: entender o framework (as regras do jogo) que originou o sistema.

Quando digo "entender", digo entender mesmo, com boa vontade. Deixar o ego de lado e entender que provavelmente a pessoa que definiu as regras do jogo conhecia BEM o jogo, talvez mais que você.

(Na itexto recentemente pegamos alguns projetos em JSF. Meu último contato com a tecnologia foi 10 anos atrás (ou mais) e não me lembrava de quase nada. Pra piorar, os livros que compramos a respeito eram simplesmente um nojo. Qual a solução? Escrevemos nosso próprio livro sobre a tecnologia que um dia será disponibilizado ao grande público (de graça), e usamos isto como um dos grandes incentivos para dominarmos a tecnologia)

E depois de entender bem este framework, verificar se pode ser aplicado na situação atual do cliente (em 90% das vezes vemos que sim). Aí é só corrigir as distorções, ou seja, pegar tudo aquilo que foge do funcionamento esperado do framework e colocá-lo nos trilhos.

(dica: se você conhece o framework fica muito fácil detectar os pontos nos quais o mesmo não foi respeitado. Eles destoam nitidamente do restante)

Muitas vezes na ânsia de entregar uma funcionalidade (ou manutenção) o desenvolvedor adota uma solução que vai contra o funcionamento esperado do framework. E com isto, naturalmente, obtém-se inúmeros problemas de desempenho, comportamentos inesperados, etc. É incrível como simplesmente aplicando os padrões do "framework" conseguimos resolver uns 90% dos problemas a um custo muito baixo.

Concluindo?

Na lista que estamos compilando de problemas este sem sombra de dúvidas é um dos que estaria no topo (talvez seja O maior causador de deterioração). Há outras causas, mas não são tão agressivas quanto esta.

Como eu poderia então descrever este comportamento em poucas palavras... já sei:

Rebeldia sem causa: ato no qual a equipe desrespeita as regras do jogo no sistema sem justificativas razoáveis.

Weissmann, Henrique Lobo

O mais assustador é que todos nós podemos ser rebeldes sem causa e sequer nos darmos conta disto. Importante manter-se atento.

PS: há outros comportamentos que vou descrever aqui neste blog, aguardem!

18