Legado — não é apenas código antigo. É um sistema funcional que traz dinheiro para o negócio, mas atrasa o desenvolvimento. No desenvolvimento móvel, o legado pode estar escrito em Objective-C, usar bibliotecas obsoletas ou padrões arquitetônicos antiquados. De acordo com um relatório da CAST Software (2024), a idade média de uma linha de código em projetos enterprise ultrapassa 14 anos. A estratégia de trabalho com legado determina se ele se torna um freio ou permanece um ativo gerenciável.
Principais pontos
Legado — código ou sistema que continua funcionando em produção mas não atende mais aos padrões modernos de qualidade. O legado pode estar escrito em uma linguagem obsoleta (por exemplo, Objective-C em vez de Swift), usar bibliotecas sem suporte ou padrões arquitetônicos considerados antipadrões.
A principal característica do legado é a ausência de testes. Pela definição de Michael Feathers (2004), código legado é código sem testes. Se não é possível alterar o comportamento com segurança, o sistema está em status de legado independentemente da idade. Código novo sem testes unitários é legado desde o primeiro dia.
Legado não é necessariamente ruim. Um sistema bem projetado em Java 8 pode ser mais confiável e compreensível do que código caótico em Kotlin com corrotinas. A idade do código não é um indicador de qualidade — o importante é quão facilmente o sistema pode ser alterado e estendido.
Todo sistema bem-sucedido torna-se legado com o tempo. É um processo natural: as tecnologias evoluem mais rápido do que o código pode ser reescrito. Um aplicativo escrito há 5 anos em Swift 2 é legado hoje, embora fosse moderno na época da criação.
O valor de negócio do legado é frequentemente subestimado. O sistema funciona de forma confiável, processa transações, armazena dados — reescrever traz riscos. De acordo com o Standish Group (2024), 35% dos projetos de reescrita completa terminam em fracasso. Economicamente, não é justificado livrar-se do legado, mas sim aprender a trabalhar com ele.
As melhores estratégias são migração gradual, encapsular o código antigo por trás de novas interfaces e testes automatizados. O legado só se torna um problema quando deixa de ser modificável a um custo previsível.
Falta de testes automatizados — o principal indicador. Se após alterar uma única linha um desenvolvedor não pode executar testes e confirmar que nada quebrou — você está diante de um legado. Um sinal adicional: o processo de implantação leva horas e requer etapas manuais.
A documentação não corresponde ao código — outro marcador. Diagramas arquitetônicos estão desatualizados, comentários descrevem um comportamento que já mudou. Tempo de ambientação para um novo desenvolvedor excede um mês — sinal de alta complexidade e baixa capacidade de manutenção.
Sinais adicionais: arquitetura monolítica sem limites claros, teste manual como método principal de verificação, pipeline de CI longo (mais de 30 minutos), uso de bibliotecas sem versões atualizadas e incapacidade de atualizar dependências sem quebrar módulos relacionados.
O fenômeno do “código frágil” — uma alteração em um lugar quebra três outros. Isso é consequência do acoplamento forte, quando os módulos sabem demais uns sobre os outros. Quanto maior o acoplamento, mais rápido o sistema transita para a categoria de legado.
Redução de velocidade — o principal risco. Adicionar uma funcionalidade simples requer horas de estudo do código e dias de teste. De acordo com Stripe (2024), os desenvolvedores gastam 33% do seu tempo superando dívida técnica, diretamente relacionada à presença de módulos legados no projeto.
Vazamento de expertise — os autores do código original saem da empresa e a documentação é incompleta. Novos desenvolvedores têm medo de tocar em módulos desconhecidos, levando ao efeito de “código congelado”: o módulo não evolui mas continua funcionando. O bus factor desses sistemas é criticamente baixo.
Segurança — bibliotecas obsoletas contêm vulnerabilidades conhecidas. Usar OpenSSL 1.0.2 ou versões antigas do Jackson em projetos Java é um caminho direto para incidentes de segurança que podem custar reputação e clientes ao negócio.
Desmotivação da equipe — trabalhar com legado sem uma estratégia de melhoria reduz a satisfação dos desenvolvedores. A equipe para de se orgulhar do produto, a rotatividade de pessoal aumenta, o que retarda ainda mais o desenvolvimento do sistema.
Testes de caracterização — o primeiro passo antes de qualquer alteração em código legado. Execute o código com dados de entrada conhecidos e registre a saída esperada. Esses testes capturam o comportamento atual como especificação. Golden master testing é uma variante onde a saída é comparada com um arquivo de referência.
Análise de seams — encontrar pontos onde o acoplamento pode ser rompido sem alterar o comportamento. Michael Feathers identifica vários tipos de seams: preprocessor seam, object seam, link seam. Object seam é o mais comum: substituir um objeto real por um stub de teste através de uma interface.
Sprout method e Sprout class — técnicas para adicionar código novo ao lado do código antigo, não dentro dele. Em vez de modificar um método existente, crie um novo método com a lógica desejada e chame-o a partir do antigo. Isso minimiza o risco de quebrar o código funcional.
class LegacyPaymentProcessor {
def process(payment) {
// 200 linhas de código legado que não devem ser tocadas
logPayment(payment) // método sprout
}
def logPayment(payment) {
// novo código adicionado junto ao legado
}
}
Padrão Strangler Fig — a abordagem recomendada para migração de legado. Um novo módulo é criado em paralelo, o tráfego é gradualmente redirecionado do antigo para o novo. O módulo antigo “morre” naturalmente quando para de receber requisições. O padrão minimiza riscos e permite rollback se surgirem problemas.
Branch by Abstraction — técnica onde uma abstração é criada sobre a implementação antiga e a nova. O código cliente muda para a abstração e a implementação antiga é gradualmente substituída. Exemplo: substituir a camada de rede de AFNetworking para Alamofire através de um protocolo unificado NetworkService.
Migração por fases — dividir a transição em pequenos passos: encapsular o módulo antigo → escrever testes → criar um novo módulo → executar em paralelo → remover o módulo antigo. Cada passo termina com um estado estável do sistema, permitindo implantação a qualquer momento.
Perguntas frequentes
A reescrita completa é a opção mais arriscada. Apenas 25% dos projetos de Big Rewrite são bem-sucedidos no prazo. É melhor aplicar o padrão Strangler Fig: substituir módulos gradualmente sem parar o produto. Cada iteração traz valor de negócio e os riscos são distribuídos ao longo do tempo.
Comece com testes de caracterização: execute o módulo com dados conhecidos, registre o resultado. Golden master testing é uma forma simples de capturar o comportamento. Adicione testes toda vez que tocar em uma linha de código. Em 6 meses você terá uma estrutura que protege contra regressões.
Se o sistema é estável, não requer mudanças frequentes e não afeta a velocidade de desenvolvimento de outros módulos — deixe-o. “Se não está quebrado, não conserte” é uma abordagem razoável para módulos legados isolados com baixa frequência de alterações. Toque no código apenas quando precisar fazer mudanças de negócio.
Use versionamento semântico e atualize em etapas: patch → minor → major. Escreva testes de compatibilidade para cada biblioteca. Dependabot ou Renovate automatizam a criação de PRs de atualização. Se uma biblioteca está obsoleta, planeje sua substituição através de uma abstração.
A dívida técnica é uma metáfora para estimar o custo de melhorias adiadas. O legado é um sistema ou código específico que já se tornou obsoleto. A dívida técnica pode acumular-se em um mês, o legado requer tempo. Nem toda dívida técnica se torna legado, mas todo legado contém dívida técnica.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também