Este tópico descreve o conceito e as classificações de código legado e apresenta estratégias para diferentes tipos. Além disso, aborda como o AI Coding Assistant auxilia na manutenção desse código.
AI Coding Assistant
Código legado
-
Código relacionado a tecnologias obsoletas:
Código associado a sistemas operacionais ou bibliotecas de software sem suporte.
Código dependente de stacks tecnológicos ou linguagens de programação obsoletos.
-
Código mantido para compatibilidade com funcionalidades legadas:
Trechos preservados para garantir compatibilidade com versões anteriores de funcionalidades em softwares modernos.
Código mantido para assegurar retrocompatibilidade.
-
Código sem documentação correspondente e sem manutenção:
Código antigo sem documentação associada.
Código que não segue práticas modernas de desenvolvimento, como testes unitários e revisões de código.
Soluções para código legado
Utilize uma das estratégias a seguir para resolver problemas relacionados a código legado:
Estratégia | Vantagens e desvantagens |
Reescrever o código | Os custos são elevados e o sistema existente pode continuar executando conforme o esperado, o que gera riscos no código. |
Refatorar o código | |
Adicionar testes unitários | Testes unitários identificam problemas no código e fornecem garantia de qualidade para possíveis alterações. |
A tabela anterior demonstra que adicionar testes unitários é um método eficaz para resolver problemas de código legado. No entanto, essa abordagem ainda apresenta os seguintes desafios:
Grande parte do código legado não possui testes unitários, e os custos de teste são muito altos devido às dependências complexas entre os códigos.
Não há métricas específicas claras para definir um teste unitário adequado.
O código que necessita de testes unitários não está claramente identificado.
Erros comuns em testes unitários
Testes unitários falsos sem asserções: Desenvolvedores podem chamar funções sem asserções para aumentar a cobertura de código, resultando em muitos testes unitários inválidos.
Testes unitários tratados como testes de caixa branca: Algumas abordagens classificam testes unitários como testes de caixa branca. Contudo, eles são, na verdade, testes de caixa preta específicos para assinaturas de funções.
Testes unitários dependentes de ambientes reais: Os principais fatores que dificultam os testes unitários incluem negligência e configurações dependentes do ambiente. Sem o uso de Stub ou Mock para eliminar a dependência de ambientes externos, como endereços IP ou bancos de dados, os testes unitários não atendem ao princípio FIRST (rápido, independente, repetível, autovalidável e oportuno).
Execução seletiva de testes unitários
Além dos benefícios, testes unitários geram custos. Ao analisar o código legado sob a perspectiva de custo-benefício, é possível complementá-lo com testes unitários. Isso significa executar testes unitários de forma seletiva. Como distinguir custos e benefícios?
Classificação por quadrantes de custo-benefício para testes unitários em código legado
É possível categorizar testes unitários específicos para código legado em quadrantes com base em seus custos e benefícios. A figura a seguir ilustra os critérios de classificação e os quadrantes dos testes unitários.

Critérios de classificação
Eixo X (Custo): Maior dependência de código resulta em maior custo de teste.
Eixo Y (Benefício): Maior complexidade de código resulta em maior ganho de qualidade.
Quatro quadrantes
|
Classificação do código |
Características |
Descrição |
Benefícios |
Custos |
|
Código de algoritmos |
Alta complexidade ciclomática e alto fan-in |
Contém muitas instruções condicionais e loops; depende de poucos trechos de código, mas é utilizado por muitos outros. |
Alto |
Baixo |
|
Código trivial Código trivial |
Baixa complexidade ciclomática e alto fan-in |
Utiliza lógica simples e contém apenas uma ou duas linhas de código. |
Baixo |
Baixo |
|
Código coordenador |
Baixa complexidade ciclomática e alto fan-in |
Está na camada superior da relação de chamadas e reflete cenários de negócios específicos ao chamar outros códigos. |
Baixo |
Alto |
|
Código excessivamente complexo |
Alta complexidade ciclomática e alto fan-in |
Utiliza lógica complexa, possui múltiplas dependências e contém muitas funções e parâmetros. É um típico code smell. |
Alto |
Alto |
Entendimento sobre complexidade ciclomática e dependências
Complexidade ciclomática: número de ramificações lógicas no código.
Fan-in: número de módulos pai que chamam diretamente o módulo. Um fan-in alto indica um elevado grau de reutilização do módulo.
Fan-out: número de módulos que um módulo chama diretamente. Um fan-out alto indica que o módulo depende de muitos outros módulos.
Estratégias para diferentes tipos de código
Com base na análise anterior, aplique as seguintes estratégias ao código legado:
Para código de algoritmos, gere testes unitários.
Para código coordenador, execute testes de API.
Para código excessivamente complexo, aproveite oportunidades adequadas para refatorar o código.
Para código trivial, não é necessário processar o código.

Use o Coding Assistant para processar código legado
1. Compreenda um projeto
Ao manter o código legado de um projeto, utilize o recurso @workspace para entender a finalidade de todo o projeto e os módulos envolvidos nele.

2. Processe diferentes tipos de código
Gere testes unitários para código de algoritmos
Selecione as seções para as quais deseja gerar código com base no código de produção. Durante a geração, concentre-se nas informações de dependência, como framework necessário e Mock. Acrescente informações relevantes após a geração de um comando de teste unitário. Por exemplo, /generate unit testingCppUTest.

Geralmente, a quantidade de casos de teste unitário gerados a partir do código existente é limitada. Caso tenha requisitos específicos quanto ao número de cenários de teste e casos de uso, gere mais testes unitários continuando as funções de teste em um arquivo de teste unitário recém-criado. Durante a continuação, o Coding Assistant segue os casos de uso existentes tanto quanto possível e os utiliza como referência de contexto.

Execute testes de API em código coordenador
Testes unitários não são a solução ideal para código coordenador, pois os custos de teste aumentam significativamente devido ao excesso de dependências. Recomendamos executar testes de API ou de funcionalidade nesse tipo de código. No entanto, desenvolvedores frequentemente enfrentam dificuldades ao escrever casos de teste automatizados. Portanto, utilize o Coding Assistant para dominar e compreender rapidamente o framework de teste.

Para escrever um caso de uso, selecione a função testada do caso de uso e faça perguntas diretamente na janela de conversação do AI Coding Assistant. Por exemplo, pergunte ao AI Coding Assistant sobre o caso de teste gerado para o código a seguir com base no Robot Framework. Dessa forma, desenvolva as palavras-chave correspondentes.
Refatore código excessivamente complexo no momento certo
Para código excessivamente complexo, utilize o comando /generate optimization do AI Coding Assistant para obter sugestões de otimização para o código selecionado. A revisão e otimização de código fornece recomendações em múltiplas dimensões, como problemas de sintaxe, melhorias no tratamento de exceções, limpeza de código, segurança e riscos.

Quanto à otimização de código legado complexo, recomendamos que os desenvolvedores não realizem otimizações e refatorações abrangentes de forma indiscriminada. Alterações em código legado podem representar riscos significativos. Assim, refatore e otimize o código no momento oportuno, conforme situações específicas, seguindo a Regra do Escoteiro. Nesse processo, o Coding Assistant também oferece forte suporte a você.
Resumo
O conteúdo anterior apresenta melhores práticas que servem de referência ao processar código legado. Para lidar eficazmente com código legado, mergulhe profundamente na estrutura complexa do código e rastreie detalhadamente cada nó de ramificação possível. Nesse processo, além de identificar e corrigir defeitos potenciais, conclua todas as tarefas dentro de um prazo limitado. Para minimizar o tempo necessário para processar código legado, recomendamos prevenir a degradação do código, fazer bom uso de ferramentas e seguir bons princípios de programação desde as fases iniciais de escrita.