João Gilberto Saraiva

desenvolvedor de software | professor | escritor


Removendo código legado: algumas dicas práticas | João Gilberto Saraiva

Removendo código legado: algumas dicas práticas

15-10-2024

Você já deve ter visto por ai a frase: “um homem sem barriga é um homem sem história”. Para além do teor cômico sobre a vida, ela nos lembra uma verdade inegável: as pessoas se transformam ao longo do tempo. Os calendários parecem avançar cada vez mais rápido enquanto mudamos de altura, peso, cabelo, roupas, etc. Transformações que não são apenas externas, envolvem humor, paciência, amor, estado mental e muitos outros aspectos. Nesse sentido, os softwares são parecidos conosco porque não são imutáveis. Um par de anos - as vezes, meses ou semanas - e uma função X ou módulo Y não faz mais tanto sentido. Se o contexto mudou, seja por conta dos usuários, regras de negócio ou desenvolvedores, é fatal que aquela aplicação passará por mudanças. Um processo que se desenrola no cotidiano e resultam na acumulação diária aqui e ali de pequenos pedaços de código que estão sem uso ou subutilizados. Como no dia em que decidimos desbravar o cantinho das coisas esquecidas do guarda-roupa ou quintal, chega o momento de remover isso que não nos serve mais. É certo que quanto maior a casa e a família, maior será o volume de objetos sem uso e o trabalho para removê-los dali. A seguir alguns passos práticos para remover essa “tralha” acumulada ao longo do tempo em grandes projetos.

No último ano atuei em atividades diversas de remoção e refatoração de código legado junto do desenvolvimento de novas funcionalidades. A seguir vão cinco dicas práticas obtidas a partir da minha experiência pessoal junto a um time da Performa IT atuando em grande projeto com dez anos de vida, o equivalente a um senhor octogenário em termos de software.

Não dirija o carro de olhos vendados

Um dos desafios para remoção e refatoração de código em grandes projetos é que não é fácil vislumbrar se um código está afetando ou não o fluxo de ações. Afinal, ele é um emaranhado de dependências e relações que não são facilmente visíveis. Por isso, é importante ter uma visão clara do que está além do para-brisas e certeza se toda a mecânica do carro funciona perfeitamente. Uma forma de fazer isso é através de testes automatizados. Eles são uma ferramenta poderosa para garantir que as mudanças que você está fazendo não estão quebrando nada. Testes em quantidade e qualidade são como pneus e pastilhas de freio novas, que garantem que o carro vai andar e parar quando você precisar. Um projeto bem provido de testes para os seus principais casos de uso é bem útil, mesmo que parte deles esteja desatualiza ou quebrada. Eles são importantes indicativos para garantir que as mudanças que se deseja fazer não esta quebrando nada. Na ausência de testes automatizados, é relevante ter um ambiente de homologação onde você possa testar as mudanças que você está fazendo antes de colocá-las em produção. Preferencialmente, um ambiente que seja o mais próximo possível do ambiente de produção e também um profissional QA para validar as mudanças que você está fazendo. De todo modo, mas é pertinente que o desenvolvedor também faça testes manuais e automatizados para ir norteando o que está sendo feito.

Se uma arma aparece na história, ela precisa ser disparada

Este é um princípio de Anton Tchekhov, um escritor russo do século XIX, que diz que elementos presentes em um enredo precisam ser utilizados. Caso contrário, ele não deveria estar lá. É uma ideia de como contar histórias muito utilizada na literatura, cinema, games e, útil também na programação. Um projeto sem código morto é um projeto mais fácil de entender e de manter. Um dos desafios em grandes projetos é que, muitas vezes, o código que você quer remover está sendo usado de modo explicito ou não por outras partes do sistema. Por isso, é importante ter uma visão clara de como o código está sendo usado e por quem para conseguir extrair o máximo de código sem uso. Uma forma de fazer isso é através de ferramentas de análise estática de código, como o Rubocop, que podem ajudar a identificar onde o código está sendo chamado e se ele está sendo usado ou não. Outra forma de fazer isso é através de logs em ambiente de homologação e produção, que podem ajudar a identificar se o código que você quer remover está sendo chamado ou não. É importante também ter um bom entendimento do negócio e do contexto do projeto, vai facilitar a sua vida. Por fim, na dúvida se o código que você quer remover está sendo usado ou não, é melhor deixar como está. Afinal, é melhor ter um código morto do que uma funcionalidade quebrada.

Brincar de apagar e acender as luzes

Uma estratégia para identificar código não utilizado é comentar o código e ver se o sistema ainda funciona. Se o os testes automatizados e/ou os fluxos em homologação continuarem funcionando, é um bom indicativo de que ele não está em uso e pode ser removido. A seguir, “descomentar” o código e testar se os erros se repetem ou não, e se novas partes param de funcionar. A estratégia de “apagar as luzes” comentando código ajuda a identificar também se outras partes da aplicação sem aparente relação com o que está sendo feito. Por exemplo, um método que subterraneamente depende de outro método que foi removido ou que não faz mais sentido. Ainda que o código esteja funcionando e em uso, ele pode estar escondendo problemas que só serão percebidos no momento da remoção. É uma oportunidade para refletir sobre a estrutura do código, verificar se ele está organizado de forma clara e coesa e refatorar o que for necessário removendo interdependências e melhorando a legibilidade.

De fora para dentro

Uma vez identificado um código que não condiz mais com as regras de negócio ou contexto do projeto e pode ser removido, uma boa estratégia é corta-lo de fora para dentro. Ou seja, começar removendo o código que está mais distante do núcleo do sistema e que tem menos dependências, como interfaces que lidam com o usuário final. A seguir, partir para estruturas intermediárias, como controladores e serviços que guiam o que acontece no sistema. Por fim, ir para o núcleo, como modelos e repositórios, que concentram funcionalidades e por vezes regras da aplicação. Se ela possuir testes para cada uma dessas camadas, eles podem ser retirados junto da respectiva fatia, fazendo pequenos desvios ou incisões para que outras partes não sejam atingidas indevidamente. A ideia é que, ao remover o código de fora para dentro, você gradualmente perceba e controle as partes afetadas na remoção, garantindo que o sistema continue funcionando conforme o esperado.

A luneta e o microscópio

Uma dica final é atentar para dois pontos de vista ao mesmo tempo: o da luneta e o do microscópio. A luneta representa o ponto de vista macro, que permite ver o sistema como uma galáxia, identificar os movimentos sistemas solares de modelos, os cometas das APIs e as crateras repletas de código parcialmente funcional nos planetas de módulos. Em um plano ainda mais amplo, estar atento as regras de negócio, integrações com outros sistemas e a arquitetura do sistema facilita muito o trabalho de se mover pela “tralha” de projetos legados, excluindo partes e dando novos sentidos a outras. Já o microscópio é o símbolo para o ponto de vista micro, que permite enxergar detalhes e identificar problemas específicos, como bugs e outro microorganismos escondidos entre as células das classes e dos métodos. Uma variável esquecida pode ser o início de um problema maior, que se diagnosticado cedo, pode ser resolvido com mais facilidade. Combinar esses dois pontos de vista permite encontrar mais facilmente padrões e tendências do código no geral e daquilo que pode ser removido em particular. Além disso, facilita pequenas e grandes refatorações, que podem ser feitas de forma mais segura e eficiente no decorrer do tempo.

Imagem: “Bricklayers, Santos Gonçalves”. Open Verse, Creative Commons 2.0