Aguarde...

4 de fevereiro de 2024

Como melhorar uma base de código legado

Como melhorar uma base de código legado

Isso acontece pelo menos uma vez na vida de todo programador, gerente de projeto ou líder de equipe. Você recebe uma pilha fumegante de esterco, se tiver sorte, apenas alguns milhões de linhas valem, os programadores originais já partiram há muito tempo para lugares mais ensolarados e a documentação – se houver alguma, para começar – está irremediavelmente fora de sincronia com o que está atualmente mantendo a empresa à tona.

Seu trabalho: tirar-nos dessa bagunça.

Depois que sua primeira resposta instintiva (correr para as colinas) passar, você inicia o projeto sabendo muito bem que os olhos da liderança sênior da empresa estão voltados para você. Falhar não é uma opção. E, no entanto, pelo que parece, o fracasso está muito presente nas cartas. Então o que fazer?

Tive a (in) sorte de estar nesta situação várias vezes e eu e um pequeno grupo de amigos descobrimos que é um negócio lucrativo ser capaz de pegar essas pilhas fumegantes de miséria e transformá-las em projetos saudáveis ​​e sustentáveis. . Aqui estão alguns dos truques que empregamos:

Cópia de segurança

Antes de começar a fazer qualquer coisa, faça um backup de tudo que possa ser relevante. Isso para garantir que nenhuma informação seja perdida que possa ser de importância crucial em algum momento. Basta uma pergunta boba que você não consegue responder para consumir um dia ou mais depois que a mudança for feita. Principalmente os dados de configuração são suscetíveis a esse tipo de problema, geralmente não são versionados e você terá sorte se forem levados junto no esquema de backup periódico. Portanto, é melhor prevenir do que remediar, copie tudo para um local muito seguro e nunca toque nele, a menos que esteja no modo somente leitura.

Pré-requisito importante, certifique-se de ter um processo de construção e que ele realmente produza o que é executado em produção

Eu perdi totalmente esta etapa, presumindo que ela é óbvia e provavelmente já está em vigor, mas muitos comentaristas da HN apontaram isso e estão absolutamente certos: a primeira etapa é ter certeza de que você sabe o que está sendo executado em produção no momento e isso significa que você precisa ser capaz de construir uma versão do software que seja – se sua plataforma funcionar dessa maneira – byte por byte idêntica à versão de produção atual. Se você não conseguir encontrar uma maneira de conseguir isso, provavelmente terá algumas surpresas desagradáveis ​​ao comprometer algo na produção. Certifique-se de testar isso da melhor maneira possível para ter certeza de que todas as peças estão no lugar e, depois de ganhar confiança suficiente de que funcionará, mova-o para a produção. Esteja preparado para voltar imediatamente para o que estava em execução antes e certifique-se de registrar tudo e qualquer coisa que possa ser útil durante a – inevitável – autópsia.

Congelar o banco de dados

Se for possível, congele o esquema do banco de dados até concluir o primeiro nível de melhorias, quando você tiver um conhecimento sólido da base de código e o código legado tiver sido totalmente deixado para trás, você estará pronto para modificar o esquema do banco de dados. Altere-o antes disso e você poderá ter um problema real em suas mãos, agora você perdeu a capacidade de executar uma base de código antiga e uma nova lado a lado com o banco de dados como base estável para construir. Manter o banco de dados totalmente inalterado permite comparar o efeito que seu novo código de lógica de negócios tem em comparação com o antigo código de lógica de negócios; se tudo funcionar como anunciado, não deverá haver diferenças.

Escreva seus testes

Antes de fazer qualquer alteração, escreva o máximo de testes de integração e de ponta a ponta que puder. Certifique-se de que esses testes produzam o resultado correto e teste toda e qualquer suposição que você possa fazer sobre como você acha que o material antigo funciona (esteja preparado para surpresas aqui). Esses testes terão duas funções importantes: ajudarão a esclarecer quaisquer equívocos em um estágio muito inicial e funcionarão como proteção quando você começar a escrever um novo código para substituir o código antigo.

Automatize todos os seus testes, se você já tiver experiência com CI, use-o e certifique-se de que seus testes sejam executados rápido o suficiente para executar o conjunto completo de testes após cada confirmação.

Instrumentação e registro

Se a plataforma antiga ainda estiver disponível para desenvolvimento, adicione instrumentação. Faça isso em uma tabela de banco de dados completamente nova, adicione um contador simples para cada evento que você possa imaginar e adicione uma única função para incrementar esses contadores com base no nome do evento. Dessa forma, você pode implementar um log de eventos com registro de data e hora com algumas linhas extras de código e terá uma boa ideia de quantos eventos de um tipo levam a eventos de outro tipo. Um exemplo: o usuário abre o aplicativo, o usuário fecha o aplicativo. Se dois eventos resultarem em algumas chamadas de back-end, esses dois contadores deverão, a longo prazo, permanecer com uma diferença constante, a diferença será o número de aplicativos abertos no momento. Se você vir muito mais aplicativos abertos do que fechados, você sabe que deve haver uma maneira de os aplicativos terminarem (por exemplo, uma falha). Para cada evento você descobrirá que existe algum tipo de relacionamento com outros eventos; normalmente você se esforçará para manter relacionamentos constantes, a menos que haja um erro óbvio em algum lugar do sistema. Seu objetivo será reduzir os contadores que indicam erros e maximizar os contadores mais abaixo na cadeia, até o nível indicado pelos contadores no início. (Por exemplo: os clientes que tentam pagar devem resultar num número igual de pagamentos reais recebidos).

Este truque muito simples transforma cada aplicativo de back-end em uma espécie de sistema de contabilidade e, assim como em um sistema de contabilidade real, os números devem corresponder, desde que você não tenha problemas em algum lugar.

Com o tempo, este sistema se tornará inestimável para estabelecer a integridade do sistema e será um ótimo companheiro próximo ao log de revisão do sistema de controle de código-fonte, onde você pode determinar o momento em que um bug foi introduzido e qual foi o efeito nos vários contadores.

Normalmente mantenho esses contadores com uma resolução de 5 minutos (ou seja, 12 buckets por uma hora), mas se você tiver um aplicativo que gera menos ou mais eventos, poderá decidir alterar o intervalo em que novos buckets são criados. Todos os contadores compartilham a mesma tabela de banco de dados e, portanto, cada contador é simplesmente uma coluna dessa tabela.

Mude apenas uma coisa de cada vez

Não caia na armadilha de melhorar a capacidade de manutenção do código ou da plataforma em que ele é executado ao mesmo tempo em que adiciona novos recursos ou corrige bugs. Isso lhe causará enormes dores de cabeça porque agora você terá que se perguntar a cada passo do caminho qual é o resultado desejado de uma ação e invalidará alguns dos testes que você fez anteriormente.

Mudanças de plataforma

Se você decidiu migrar o aplicativo para outra plataforma, faça isso primeiro, mas mantenha todo o resto exatamente igual . Se quiser você pode adicionar mais documentação ou testes, mas não mais do que isso, toda lógica de negócio e interdependências devem permanecer como antes.

Mudanças de arquitetura

A próxima coisa a fazer é alterar a arquitetura do aplicativo (se desejado). Neste momento você está livre para alterar a estrutura de nível superior do código, geralmente reduzindo o número de links horizontais entre módulos e, assim, reduzindo o escopo do código ativo durante qualquer interação com o usuário final. Se o código antigo fosse de natureza monolítica, agora seria um bom momento para torná-lo mais modular, dividir funções grandes em funções menores, mas deixar os nomes das variáveis ​​e estruturas de dados como estavam.

O usuário HN mannykannot aponta – com razão – que isso nem sempre é uma opção; se você tiver muito azar, talvez seja necessário se aprofundar para poder fazer alterações na arquitetura. Eu concordo com isso e deveria ter incluído aqui, daí esta pequena atualização. O que eu gostaria de acrescentar é que se você fizer alterações de alto e baixo nível, pelo menos tente limitá-las a um arquivo ou, na pior das hipóteses, a um subsistema, para limitar o escopo de suas alterações tanto quanto possível. Caso contrário, você poderá ter muita dificuldade em depurar a alteração que acabou de fazer.

Refatoração de baixo nível

Até agora você deve ter um bom entendimento do que cada módulo faz e está pronto para o trabalho real: refatorar o código para melhorar a capacidade de manutenção e deixar o código pronto para novas funcionalidades. Esta provavelmente será a parte do projeto que consumirá mais tempo, documente à medida que avança, não faça alterações em um módulo até que você o tenha documentado completamente e sinta que o compreende. Sinta-se à vontade para renomear variáveis ​​e funções, bem como estruturas de dados para melhorar a clareza e consistência, adicionar testes (também testes unitários, se a situação os justificar).

Corrigir bugs

Agora que você está pronto para enfrentar as mudanças reais visíveis para o usuário final, a primeira ordem de batalha será a longa lista de bugs que se acumularam ao longo dos anos na fila de tickets. Como de costume, primeiro confirme se o problema ainda existe, escreva um teste para esse efeito e depois corrija o bug. Seu CI e os testes ponta a ponta escritos devem mantê-lo protegido contra quaisquer erros que você cometa devido à falta de compreensão ou algum questão periférica.

Atualização de banco de dados

Se necessário, depois de tudo isso ser feito e você estiver em uma base de código sólida e sustentável novamente, você terá a opção de alterar o esquema do banco de dados ou substituir o banco de dados por uma marca/modelo completamente diferente, se for isso que você planejou fazer. Todo o trabalho que você fez até agora ajudará você a fazer essa mudança de maneira responsável e sem surpresas. Você pode testar completamente o novo banco de dados com o novo código e todos os testes em vigor para garantir sua migração sai sem problemas.

Execute no roteiro

Parabéns, você está fora de perigo e agora está pronto para implementar novas funcionalidades.

Nunca tente reescrever big bang

Uma reescrita big bang é o tipo de projeto que tem quase certeza de fracasso. Por um lado, para começar, você está em um território desconhecido, então como você saberia o que construir; por outro, você está empurrando todos os problemas para o último dia, um dia antes de entrar em operação com seu novo sistema. E é aí que você falhará, miseravelmente. As suposições da lógica de negócios se revelarão falhas, de repente você terá uma ideia de por que aquele sistema antigo fez certas coisas da maneira que fez e, em geral, você acabará percebendo que os caras que montaram o sistema antigo talvez não fossem afinal, idiotas. Se você realmente deseja destruir a empresa (e ainda por cima sua própria reputação), faça uma reescrita radical, mas se você for esperto, isso nem sequer está na mesa como uma opção.

Então, a alternativa, trabalhar de forma incremental

Para desembaraçar uma dessas bolas de cabelo, o caminho mais rápido para a segurança é pegar qualquer elemento do código que você entenda (pode ser um bit periférico, mas também pode ser algum módulo central) e tentar melhorá-lo gradativamente ainda dentro do antigo contexto. Se as ferramentas de construção antigas não estiverem mais disponíveis, você terá que usar alguns truques (veja abaixo), mas pelo menos tente deixar vivo o máximo do que é conhecido por funcionar enquanto você inicia suas alterações. Dessa forma, à medida que a base de código melhora, também melhora a sua compreensão do que ela realmente faz. Um commit típico deve ter no máximo algumas linhas.

Liberar!

Cada mudança ao longo do caminho é liberada para produção, mesmo que as mudanças não sejam visíveis para o usuário final, é importante realizar os menores passos possíveis porque, enquanto você não entender o sistema, há uma boa chance de que apenas o ambiente de produção seja dizer que há um problema. Se esse problema surgir logo após fazer uma pequena alteração você ganhará diversas vantagens:

  • provavelmente será trivial descobrir o que deu errado
  • você estará em uma excelente posição para melhorar o processo
  • e você deve atualizar imediatamente a documentação para mostrar os novos insights obtidos

Use proxies a seu favor

Se você está desenvolvendo web, louve aos deuses e insira um proxy entre os usuários finais e o sistema antigo. Agora você tem controle por URL sobre quais solicitações vão para o sistema antigo e quais serão redirecionadas para o novo sistema, permitindo um controle muito mais fácil e granular sobre o que é executado e quem pode vê-lo. Se o seu proxy for inteligente o suficiente, você provavelmente poderá usá-lo para enviar uma porcentagem do tráfego para o novo sistema para uma URL individual até estar satisfeito com o funcionamento das coisas como deveriam. Se seus testes de integração também se conectarem a esta interface será ainda melhor.

Sim, mas tudo isso levará muito tempo!

Bem, isso depende de como você encara as coisas. É verdade que há um pouco de retrabalho envolvido na execução dessas etapas. Mas funciona , e qualquer tipo de otimização desse processo pressupõe que você sabe mais sobre o sistema do que provavelmente sabe. Tenho uma reputação a manter e realmente não gosto de surpresas negativas durante trabalhos como este. Com alguma sorte a empresa já está em declínio, ou talvez haja um perigo real de bagunçar as coisas para os clientes. Em uma situação como essa, prefiro o controle total e um processo rígido a economizar alguns dias ou semanas, se isso colocar em risco um bom resultado. Se você gosta mais de coisas de cowboy – e seus chefes concordam – então talvez fosse aceitável correr mais riscos, mas a maioria das empresas preferiria seguir o caminho um pouco mais lento, mas muito mais seguro, para a vitória.

Postado em BlogTags:
Escreva um comentário