Código legado é aquele que ainda cumpre sua função, mas foi desenvolvido com tecnologias hoje ultrapassadas. Ele abrange códigos herdados de outras equipes ou versões antigas do software, além de código-fonte que não recebe mais suporte nem manutenção ativa. Também engloba códigos criados com hardware obsoleto ou sistemas operacionais, compiladores descontinuados, interfaces de programação de aplicativos (APIs) antigas, linguagens de programação ultrapassadas ou ambientes de desenvolvimento de software desatualizados. Como resultado, o código legado deixa de atender aos padrões atuais de codificação, aos princípios modernos de design de software e à arquitetura computacional mais recente.
No livro Working Effectively with Legacy Code, publicado em 2004, Michael Feathers apresentou outra definição: “código sem testes”.1 Essa definição de código legado significa que os programadores não têm como verificar se o código funciona e se funciona conforme o esperado. Muitos sistemas legados também carecem de documentação adequada, o que dificulta entender seu funcionamento e torna a tarefa de expandi-los ou melhorá-los ainda mais desafiadora.
O código legado gera dívida técnica, que precisa ser “paga” com o tempo por meio da manutenção contínua do código. Veja alguns desafios comuns que as organizações podem enfrentar ao manter código legado:
● Adaptabilidade
● Custo
● Desempenho
● Escalabilidade
● Segurança e conformidade
Por ser ultrapassado, o código legado pode ser incompatível ou difícil de integrar a sistemas mais modernos. A falta de adaptabilidade pode dificultar a inovação e desacelerar o crescimento dos negócios, fazendo com que as empresas percam vantagem competitiva.
A manutenção de sistemas legados pode ser cara. Esses custos operacionais e de manutenção podem se acumular, especialmente com o aumento das taxas de suporte terceirizado para versões antigas de software e hardware. Além disso, encontrar desenvolvedores com experiência em práticas antigas de computação ou linguagens obsoletas pode ser difícil e custoso.
Arquiteturas monolíticas e desajeitadas resultam em alta latência, tempos de resposta lentos e downtime frequente. Esse desempenho lento compromete a experiência do usuário e reduz a satisfação dos clientes. Isso também pode prejudicar a produtividade e a eficiência dos membros da equipe que trabalham com esses sistemas e cuidam da manutenção.
Sistemas desatualizados podem ter dificuldades diante do aumento da carga de usuários. Eles enfrentam dificuldades para atender a picos de demanda e para se ajustar de forma escalável conforme necessário. Como os componentes são fortemente acoplados, fica difícil atualizar funcionalidades existentes ou incluir novos recursos.
Códigos antigos nem sempre recebem atualizações de segurança nem seguem os padrões atuais, tornando-se vulneráveis a ciberataques e violações. Sistemas legados também podem estar em desacordo com as regulamentações atuais.
Modernizar aplicações legadas exige um planejamento cuidadoso. Veja uma metodologia com 5 etapas para tornar esse processo mais eficiente:
● Compreender a base de código
● Dividir e conquistar
● Criar testes de caracterização
● Refatorar, migrar ou reescrever
● Testar e documentar
O primeiro passo é entender a base de código, e essa geralmente é a parte mais difícil. Comece analisando toda a documentação disponível, como requisitos, comentários no código ou o histórico de versões, incluindo registros de commits e logs de alterações.
Se a documentação for insuficiente, utilize ferramentas de análise estática que examinam o código automaticamente sem executá-lo. Além disso, as ferramentas de visualização de código podem gerar representações gráficas da estrutura do código-fonte, facilitando o mapeamento de dependências e interações entre os elementos.
Quando as equipes de desenvolvimento de software tiverem uma compreensão suficiente do sistema legado, elas podem começar a lidar com ele. Bases de código extensas podem ser difíceis de lidar, por isso é melhor dividi-las em módulos menores e mais gerenciáveis, trabalhando em um por vez.
Geralmente, os testes servem para validar se o código funciona conforme o planejado. Já os testes de caracterização servem para entender o que o código faz e como ele funciona. Isso é útil para entender o código legado.2
As empresas geralmente têm três opções para modernizar código legado: refatorar, migrar ou reescrever. Também é possível combinar essas abordagens. A escolha do caminho a seguir deve contar com a participação tanto da equipe de engenharia de software quanto da liderança empresarial.
A refatoração de código modifica a estrutura interna do código-fonte sem alterar seu comportamento externo nem afetar sua funcionalidade. Essas pequenas mudanças têm menos chance de gerar bugs e ajudam a deixar o código mais claro, limpo e fácil de manter.
No caso de código legado, as equipes podem começar com pequenas modificações em cada módulo, como renomear variáveis, remover métodos duplicados ou não utilizados e padronizar a formatação. Em seguida, podem avançar para reestruturações mais lógicas, como dividir métodos grandes em menores, simplificar condicionais complexas e redistribuir funcionalidades entre funções para reduzir dependências e aumentar a coesão.
A migração é outro caminho para modernizar o código legado. Isso envolve migrar todo o código ou partes dele para plataformas ou stacks mais modernos, como a transição de uma arquitetura monolítica para microsserviços ou de uma infraestrutura no local para a nuvem. É essencial verificar a compatibilidade com a plataforma ou stack adotada e confirmar se há suporte oferecido pelos fornecedores durante a migração.
Reescrever costuma ser a última alternativa, pois implica escrever um novo código do zero para substituir o antigo. Essa abordagem representa um novo projeto por si só, um esforço considerável que pode exigir uma equipe de desenvolvimento exclusiva.
Tanto a migração quanto a reescrita podem ser tarefas difíceis diante de grandes bases de código legado, por isso as equipes podem considerar a estratégia do “estrangulamento progressivo”.3 Uma figueira estranguladora cresce no alto da superfície de uma árvore, com suas raízes descendo até o chão, envolvendo lentamente sua árvore hospedeira em uma estrutura restritiva que eventualmente faz com que ela murche.
No contexto de sistemas legados, as equipes podem migrar ou reescrever fragmentos de código aos poucos até que toda a base esteja em um framework moderno ou em uma linguagem de programação atual. No entanto, as equipes precisam criar uma arquitetura de transição para que o código existente e o novo possam coexistir. Essa arquitetura de transição será desativada assim que a migração ou reescrita estiver concluída.3
É fundamental testar minuciosamente o código refatorado, migrado ou reescrito para garantir que nenhum bug surja. Os desenvolvedores podem criar testes de unidade e de integração, mas é igualmente importante envolver a equipe de QA para realizar testes funcionais, de regressão e de ponta a ponta, garantindo a integridade das funcionalidades e do comportamento do sistema.
A documentação é outra parte crítica do fluxo de trabalho de modernização. Documente as alterações do código-fonte, seja anotando o código por meio de comentários integrados, criando logs de alterações detalhados ou escrevendo documentos abrangentes de arquitetura e design e outras documentações técnicas.
Várias ferramentas podem ajudar a acelerar e automatizar o processo de modernização do código legado. Veja aqui os mais comuns:
● Analisadores de código estático
● Aplicações de visualização de código
● Frameworks de automação de testes
● Plataformas e toolkits de migração
● Geradores de documentos
Analisadores estáticos podem auxiliar na depuração de código legado, identificando falhas de programação, problemas de qualidade e até vulnerabilidades de segurança. Muitas ferramentas de análise estática de código oferecem suporte a linguagens legadas como C, COBOL, PL/I e RPG. Entre os exemplos de analisadores estáticos de código estão CodeSonar, Klocwork, o PMD de código aberto e o SonarQube.
Os visualizadores de código representam o código-fonte graficamente, oferecendo uma visão mais clara de seu funcionamento, especialmente em bases grandes ou complexas. Essas representações gráficas podem assumir diferentes formatos, como mapas de código, fluxogramas e diagramas UML (unified modeling language). Exemplos de aplicativos de visualização de código incluem CodeScene, CodeSee e Understand, entre outras.
Esses frameworks criam e executam testes automatizados, além de gerar relatórios com os resultados. Entre os frameworks mais usados para automação de testes estão o Cypress e o Selenium para aplicações web e o Appium para aplicativos móveis.
Essas plataformas e toolkits ajudam a simplificar e automatizar os fluxos de migração de sistemas legados. Entre as principais plataformas de migração estão o AWS Application Migration Service, o Azure Migrate, os toolkits de migração do Google Cloud, o IBM Cloud Transformation Advisor e o Red Hat Migration Toolkit for Applications.
Essas ferramentas geram automaticamente documentação a partir do código-fonte e de outros arquivos de entrada. Exemplos de ferramentas de geração de documentação incluem Doxygen, Sphinx e Swimm, entre outras.
A inteligência artificial (IA) pode ajudar na modernização de código legado. Aplicações de IA generativa são baseadas em grandes modelos de linguagem (LLMs), capazes de analisar bases legadas complexas ou extensas.
A IA generativa pode ser usada para auxiliar nas seguintes tarefas de modernização de código legado:
● Explicação do código
● Refatoração de código
● Transformação de código
● Geração de testes e documentação
A IA generativa consegue compreender o contexto e a semântica por trás de bases legadas de código. Com isso, essas ferramentas conseguem descrever a lógica e a função por trás do código, explicando-o de forma acessível para os programadores.
Ferramentas impulsionadas por IA podem fornecer sugestões de refatoração em tempo real. Por exemplo, o IBM ® watsonx Code Assistant utiliza modelos do IBM® Granite para identificar bugs e otimizações. Em seguida, ele propõe correções específicas alinhadas às convenções de codificação da equipe, o que simplifica e acelera a refatoração do código.
Os sistemas de IA podem indicar formas de implementar o código-fonte de uma linguagem legada para uma linguagem mais moderna. Por exemplo, o IBM watsonx Code Assistant for Z combina automação e IA generativa para ajudar desenvolvedores a modernizar aplicações em mainframe. Esses recursos de IA generativa incluem explicação de código em COBOL, JCL e PL/I, além da conversão de COBOL para código em Java.
Assim como os frameworks de automação de testes, os assistentes de codificação com IA também podem gerar testes automaticamente. Eles também conseguem criar comentários no código para documentar a função de determinados trechos ou blocos.
Como acontece com qualquer aplicação impulsionado por IA, os programadores ainda precisam ter cuidado ao usar a IA para modernizar o código legado. Eles devem analisar a precisão dos resultados e testar quaisquer alterações ou correções sugeridas.
O Instana simplifica sua jornada de migração para a nuvem, oferecendo monitoramento abrangente e insights praticáveis.
Aproveite a IA generativa para modernização acelerada e simplificada de aplicações de mainframe.
Otimize as aplicações legadas com a nuvem híbrida e os serviços e estratégias de modernização orientados por IA.
1 #195 - Working Effectively with Legacy Code and AI Coding Assistant - Michael Feathers, Tech Lead Journal, 14 de outubro de 2024
2 Characterization Testing, Michael Feathers, 8 de agosto de 2016
3 Strangler Fig, Martin Fowler, 22 de agosto de 2024