Ir para o conteúdo
14 de ago. de 2026·6 min de leitura

Como medir código morto sem apagar regras de negócio?

Aprenda a medir código morto com alcance estático, cobertura em execução, repetição de tráfego e testes de regras raras de fim de ano.

Como medir código morto sem apagar regras de negócio?

Código morto não é tudo o que parece velho, estranho ou silencioso em produção. É código sobre o qual você consegue fazer uma afirmação específica, apoiada por evidências, a respeito de alcance, observação e responsabilidade de negócio. Essas afirmações são diferentes. Juntá-las sob um rótulo único leva uma limpeza ou reescrita a remover a única implementação de um ajuste anual, de um contrato inativo ou de uma rotina de recuperação.

Já vi equipes apagarem rotinas porque ninguém reconhecia seus nomes e depois descobrirem que um agendador as chamava com uma conta de serviço no último dia útil do ano. Não havia chamada recebida da aplicação nem execução em uma amostra normal de tráfego, mas existia um chamador real. Um bom método precisa lidar com esse caso.

Trate o suposto código morto como um problema de evidência. A análise estática mapeia o que pode ser chamado. A cobertura em execução registra o que foi chamado em condições definidas. Catálogos de jobs, calendários, configuração, valores de dados, procedimentos operacionais e arquivos de tráfego mostram entradas que nenhum dos dois métodos vê sozinho. Só apague quando os registros concordarem, um responsável aceitar a conclusão e um teste de paridade não mostrar perda de comportamento.

Código morto tem três significados diferentes

Um relatório deve separar caminhos impossíveis, caminhos não observados e comportamento obsoleto. Cada categoria permite uma ação diferente. Um percentual único esconde a incerteza que o revisor precisa ver.

Código estaticamente inalcançável não tem caminho possível a partir de nenhum ponto de entrada declarado no modelo do analisador. Uma função privada sem referência em um módulo fechado pode entrar aqui, assim como um ramo cuja condição o sistema de tipos prova falsa. É o sinal técnico mais forte, mas depende da lista de entradas e do modelo de despacho.

Código não observado em execução não recebeu acessos durante uma janela e uma carga nomeadas. Isso não diz nada sobre outras datas, clientes, papéis, faixas de dados, falhas ou ações de operadores. O tráfego web normal costuma perder jobs em lote, recuperação, telas administrativas, migrações e o ramo que trata um saldo negativo após estorno. Chame de não observado, nunca de inalcançável.

Código obsoleto para o negócio ainda roda ou continua alcançável, mas a empresa não precisa mais do resultado. Uma regra tributária para um país abandonado talvez seja obsoleta. Um cálculo de produto encerrado pode continuar necessário para corrigir contas antigas. Só um responsável de negócio, com fatos contratuais e de retenção, pode decidir. Um compilador não pode.

Use um campo de estado em vez de um booleano:

  • inalcançável no modelo M
  • não observado na carga W durante a janela T
  • mantido para o evento raro E
  • obsoleto pela decisão D
  • desconhecido, investigar

Esse vocabulário evita uma troca comum. Engenheiros começam com zero acessos, falam em código morto e aprovam a exclusão como se a impossibilidade estivesse provada. As palavras mudaram, a evidência não.

Alcance estático prova impossibilidade, não desuso

A análise estática funciona melhor para provar que o fluxo normal não alcança um símbolo a partir de raízes conhecidas. Ela fica fraca quando um sistema legado transforma nomes e dados em controle. O resultado sempre deve registrar raízes, regras de resolução e pontos cegos.

Liste todos os pontos de entrada legítimos, não apenas o executável principal. Em mainframe, inclua transações, etapas JCL, módulos chamados, exits, gatilhos de banco e utilitários operacionais. No AS/400, programas CL e descrições de jobs podem chamar RPG sem caminho interativo. Sistemas desktop acrescentam entradas COM, macros e associações de arquivos. Monólitos web acrescentam scripts agendados, consumidores de filas, hooks e rotas montadas por configuração.

Depois monte um grafo de chamadas com confiança em cada aresta. Uma chamada direta resolvida pelo compilador é mais forte que uma string parecida com o nome de um procedimento. Mantenha visíveis as chamadas indiretas não resolvidas. Ponteiros, reflexão, injeção de dependência, ligação tardia, SQL gerado, procedures e registros de plugins criam arestas que uma busca textual perde.

A remoção feita pelo linker ou compilador responde a uma pergunta menor. Os manuais descrevem seções ou instruções sem efeito em uma compilação específica. Isso ajuda no tamanho do binário, mas não prova que a fonte pode sair de outras variantes, módulos carregados, scripts ou entradas operacionais. O binário otimizado não é o modelo inteiro da aplicação.

Um artefato pequeno e revisável vale mais que um grafo colorido com dez mil nós. Exporte uma linha por candidato com símbolo, local, raízes, chamadas diretas, referências indiretas possíveis, variantes e versão do analisador. Se uma aresta depender de string, chave, linha do banco ou nome de job, guarde a evidência junto.

Um achado só é forte depois de responder: quais raízes entraram, quais linguagens e artefatos gerados foram lidos, como chamadas indiretas foram resolvidas e quais componentes ficam fora do repositório? Qualquer resposta desconhecida deixa o achado provisório.

Cobertura prova execução, não segurança

A cobertura em execução prova que o código rodou em uma carga gravada, mas zero acessos não provam que ele é desnecessário. Cobertura testemunha presença, não demonstra ausência.

GNU gcov informa quantas vezes linhas e ramos rodaram em um programa instrumentado. Coverage.py faz distinção parecida em Python e registra destinos dos ramos. Os dois manuais vinculam os resultados às execuções fornecidas. A condição importa mais que o percentual: uma execução cobre apenas suas entradas, ambiente, datas, identidades e falhas.

Cobertura de linha também perde detalhes. Uma condição composta pode rodar sem um operando mudar. Um switch pode passar com um caso intacto. Cobertura de ramos melhora a evidência, mas não diz se a saída ou efeito estava correto. Para excluir, colete ramos ou arestas e compare saídas.

Instrumente toda superfície encontrada pela análise estática. Tráfego interativo raramente basta. Inclua lotes, workers, tarefas agendadas, relatórios, rotinas de banco, recuperação e ferramentas administrativas. Antes de juntar dados, preserve versão, entrada, job, cliente ou unidade, papel, data e fonte da carga. Um mapa fundido esconde que uma linha só rodou no fechamento anual.

A janela deve seguir o calendário do negócio, não um número conveniente de dias. Cubra tarefas diárias, semanais, mensais, fechamentos, renovações, vencimentos, horário de verão, ano bissexto e relatórios regulatórios reais. Se esperar for inviável, repita uma carga gravada ou recrie o evento isoladamente. Três dias movimentados não representam um exercício.

A instrumentação pode alterar tempo, memória e falhas. Meça o custo perto de corridas, timeouts e limites do lote. Onde for insegura, use traces amostrados, contadores, auditoria do banco ou logs existentes. Evidência mais fraca serve quando recebe o nome certo e se combina com outras fontes.

O tempo faz parte da entrada

Código raro de calendário está ativo e recebe uma data, um corte ou um estado acumulado. Equipes o perdem quando tratam tempo como pano de fundo.

Considere uma alocação de fim de ano. A aplicação lança operações normais o ano todo. No último dia útil, um agendador inicia um lote depois do fechamento do razão. JCL passa um modo, uma tabela escolhe contas diferidas e o programa gera ajustes no período seguinte. Nenhuma requisição web chama a rotina. Ninguém reconhece seu nome abreviado. Onze meses de cobertura mostram zero.

A reescrita apaga a rotina e substitui o serviço contábil. Testes comuns passam porque usam março e junho. No fechamento, os totais ainda fecham e as verificações simples também passam. O defeito aparece depois, quando extratos divergem da regra contratual. A regra perdida era uma combinação programada de data, controle do job, estado da tabela e período de saída.

Crie um inventário de eventos ao lado do grafo. Pergunte a finanças, operações, suporte e conformidade, mas também examine agendadores, JCL, cron, histórico, manuais, tabelas de controle, calendários, chegada de arquivos e datas de arquivos antigos. Procure comparações de datas, períodos, feriados, modos especiais e anos de corte.

Registre a última execução e a próxima oportunidade esperada. Uma rotina vista no último fechamento tem explicação. Outra sem acessos por quatro anos pode cobrir correções de cinco anos. Um job noturno pode entrar em um ramo só quando existe uma linha de controle. A frequência do chamador não define a de cada regra.

A troca do relógio merece uma interface de teste. Passe o tempo da aplicação por uma fonte controlável e congele de forma coerente o relógio do banco e do agendador. Se um componente usar a data simulada e outro o host, o teste cria estados impossíveis.

Monte um registro de evidências antes de apagar

Troque palpite por paridade
Nosso harness compara o sistema reescrito ao original com seu tráfego.

Um registro transforma um debate vago em conclusões que outro engenheiro reproduz. Ele deve acompanhar a migração, receber revisão como código e guardar referências brutas.

Uma linha por símbolo ou função coerente basta. Anote ID, símbolo, estado estático, raízes, arestas pendentes, acessos, janela, cargas, evento raro, chamadores externos, responsável, decisão e locais das provas. Não transforme desconhecido em zero. Zero significa medido e ausente; desconhecido significa não medido.

Um repositório de cobertura produz uma lista útil com SQL comum. Ajuste nomes, mas mantenha dimensões:

SELECT s.symbol_id, s.qualified_name,
       COALESCE(SUM(c.hit_count), 0) AS hits,
       MIN(c.observed_at) AS first_seen,
       MAX(c.observed_at) AS last_seen,
       COUNT(DISTINCT c.workload_id) AS workloads
FROM symbols s
LEFT JOIN coverage_events c ON c.symbol_id = s.symbol_id
WHERE s.release_id = :release_id
GROUP BY s.symbol_id, s.qualified_name
ORDER BY hits, s.qualified_name;

A saída deve ser simples: símbolo, acessos, primeira e última observação e número de cargas. Junte uma tabela que marque tráfego online, fim do mês, fim do ano, recuperação, administração ou repetição. Um zero baseado apenas em web não condena um símbolo de lote.

Adicione uma tabela de decisão:

Resultado estáticoResultado em execuçãoEvidência de negócioAção
InalcançávelSem acessosSem entrada externaIsolar e testar exclusão
AlcançávelSem acessosExiste evento raroManter e testar
AlcançávelCom acessosResponsável diz obsoletoConfirmar chamadas e retirar
DesconhecidoSem acessosDesconhecidaInvestigar, não apagar

A primeira linha ainda exige compilação e teste comportamental. Código gerado, variantes e empacotamento podem abrir o grafo. A terceira também exige cuidado: chamadores podem depender de efeitos colaterais. Retire juntos o caminho e suas obrigações de dados.

Execute caminhos raros de propósito

Um caminho raro merece uma carga dirigida, não a esperança de que produção o cubra. Derive testes do inventário e defina saídas capazes de detectar a regra ausente.

Tráfego gravado preserva combinações que dados sintéticos não antecipam. Limpe ou tokenize campos sensíveis, preserve a ordem quando o estado depender dela e capture tabelas, relógio e arquivos. Uma requisição sem estado de banco muitas vezes não é reproduzível. Em lotes, arquive entradas, parâmetros, códigos, arquivos, mudanças e mensagens do operador.

Acrescente limites sintéticos: véspera, data e dia seguinte; entrada vazia; menor conta válida; estorno; conta criada tarde; reinício após processamento parcial. Eles testam seleção, cálculo, persistência e recuperação separadamente.

Compare limites observáveis: HTTP, gravações, arquivos, mensagens, códigos de saída, arredondamento, ordem e janelas de repetição. Normalize IDs variáveis, não valores de negócio. Em registros de largura fixa, compare posições e preenchimento, pois o consumidor pode depender dos bytes.

Não persiga um percentual. Uma suíte alta pode perder o ramo do período fiscal 13. Ligue cada candidato à carga que deve alcançá-lo ou prove por que nenhuma pode. A unidade útil é um símbolo explicado.

Se um caminho não puder rodar com segurança, crie um teste de caracterização abaixo dele. Chame o cálculo com entradas capturadas, rode a procedure em banco restaurado ou invoque o lote com parâmetros reais. Documente com precisão a fronteira não testada.

A exclusão deve ser um experimento controlado

Encontre o lote silencioso
A análise completa mantém visíveis tarefas agendadas e chamadas entre linguagens.

Apague em unidades pequenas e reversíveis e deixe o sistema refutar a conclusão. O melhor teste remove o candidato, recompila variantes, repete cargas e compara todos os efeitos com a base.

Se as dependências estiverem emaranhadas, isole primeiro. Coloque um adaptador único, remova entradas duplicadas e adicione contadores na fronteira. A estrutura muda sem comportamento, criando um ponto melhor de medição. Ausência de chamadas em todos os eventos fortalece a exclusão.

Guarde quatro artefatos: linha do registro, diff exato, saídas base e saídas após remoção. Rode testes, destinos alternativos, lotes, administração e tráfego. Verifique mutações e arquivos, não apenas sucesso. Código de retorno zero pode acompanhar um lançamento ausente.

Comparação em sombra ajuda em cálculos sem efeitos. Execute original e substituto com a mesma entrada, bloqueie gravações de um lado e compare. Não duplique cobranças, avisos, reservas ou estado compartilhado sem destino seguro.

O conselho de apagar tudo com zero acessos após prazo fixo é popular por produzir uma métrica simples. Está errado com calendário, contratos inativos, recuperação manual ou despacho por dados. Defina exigências por classe: validação online precisa de papéis e clientes representativos; fechamento precisa de uma carga fiel.

Mantenha reversão real. Um revert não basta se você mudar dados, remover coluna, parar arquivo ou alterar contrato de mensagem. Adie esquemas destrutivos e preserve compatibilidade até obter evidência dos consumidores.

A reescrita preserva comportamento antes do desenho

Modernize o comportamento
CodeHero preserva resultados ao migrar para Go, Rust, TypeScript e Postgres.

A reescrita deve classificar e testar comportamento antes de decidir o que implementar. Traduzir toda rotina alcançável preserva estrutura acidental; apagar toda rotina suspeita perde regras. Primeiro vem a paridade das obrigações, depois a mudança arquitetural.

Monte o harness com observações de produção e eventos raros. Dê aos dois sistemas as mesmas entradas ordenadas e estado inicial. Compare respostas, estado durável, arquivos, mensagens e falhas. Se a interface mudar, compare uma representação canônica de negócio em vez de copiar módulos internos.

A diferença entre paridade de código e comportamento é decisiva. Um serviço Go não precisa dos parágrafos COBOL nem das globais VB6. Precisa manter valores, decisões, arredondamento e recuperação até a aprovação de uma mudança. CodeHero usa tráfego de produção gravado em um harness de paridade enquanto moderniza a arquitetura, o nível certo de comparação.

Dê uma asserção negativa ao comportamento retirado. Se um relatório sair, teste que nenhum job o agenda e nenhum arquivo aparece. Se uma regra terminar, inclua um registro antes elegível e confira o tratamento aprovado. A ausência fica testável em uma fronteira definida.

Não apague a procedência. Ligue cada regra implementada, alterada ou omitida ao registro. O revisor deve encontrar carga, responsável e efeito que explicam um ramo, além das provas que explicam o sumiço de uma rotina.

Defina uma regra de exclusão aplicável

Uma política funciona se especificar evidência, autoridade e condição de parada. Escreva para que o revisor rejeite uma mudança sem discutir se o código parece velho.

Um portão defensável pode exigir:

  1. Nenhum caminho recebido sem explicação no inventário completo, ou todos os chamadores restantes estão na retirada.
  2. A execução cobre cargas e eventos relevantes, com dados brutos preservados.
  3. Chamadores externos, configuração, agendador, despacho por dados e procedimentos foram verificados.
  4. Um responsável aprovou a obsolescência, ou a prova técnica mostrou impossibilidade.
  5. A retirada passou compilações, testes, repetição e comparação de efeitos, com plano de reversão.

O portão deve aceitar desconhecido. Algumas rotinas dependem de restaurar um arquivo ou reconstruir um fechamento. Marcá-las identifica a evidência ausente. Apagá-las para melhorar um percentual não é progresso.

Meça candidatos investigados, caminhos impossíveis, caminhos raros testados, comportamento retirado com aprovação, desconhecidos e exclusões aprovadas pela paridade. Não premie linhas removidas. Uma regra anual de dez linhas pode carregar mais obrigações que dez mil linhas de telas abandonadas.

Pegue uma rotina supostamente morta e escreva a afirmação exata. Se ela só diz que ninguém a viu rodar, você mediu familiaridade. Acrescente raízes, cargas, eventos, responsáveis e efeitos até outro engenheiro reproduzir a conclusão. Só então a tecla de apagar entra no processo.

Perguntas frequentes

Qual é a diferença entre código morto e não usado?

Código morto tem prova de que nenhuma execução necessária o alcança. Não usado costuma indicar apenas que uma ferramenta ou janela não encontrou uso, mesmo com possíveis chamadores ou eventos raros.

A análise estática prova que um código está morto?

Pode provar inacessibilidade dentro de um modelo definido. Reflexão, chamadas geradas, configuração, agendadores, gatilhos e componentes externos limitam a prova.

Cobertura zero torna uma exclusão segura?

Não. Só indica que a função não rodou nas cargas e datas medidas. Você ainda precisa revisar alcance, eventos raros, chamadores externos e decisão de negócio.

Por quanto tempo a cobertura deve ser medida?

Siga o calendário do negócio, não um prazo fixo. A janela ou repetição precisa incluir jobs, fechamentos, renovações, vencimentos, recuperação e processos anuais relevantes.

Como encontrar código que só roda no fim do ano?

Examine histórico, JCL, definições de jobs, procedimentos, tabelas, arquivos e condições de data. Recrie o fechamento com parâmetros e estado reais, depois meça ramos e saídas.

Código removido pelo compilador conta como fonte morta?

Não automaticamente. O compilador prova o que uma compilação pode omitir. Outras opções, módulos, scripts e entradas podem continuar precisando da fonte.

O que entra em um registro de evidências?

Registre símbolo, entradas, arestas pendentes, cargas, datas, eventos, referências, responsável, decisão e provas. Mantenha desconhecido separado de zero.

Como testar a exclusão de código legado?

Capture a base, remova um candidato pequeno, recompile variantes e repita cargas. Compare estado, arquivos, mensagens, códigos e falhas, não só respostas.

Cobertura alta basta para uma reescrita?

Não. Um total alto pode perder um ramo fiscal ou de recuperação. Ligue comportamentos a cargas e compare efeitos antigos e novos.

Quem aprova a retirada de uma regra antiga?

Um responsável de negócio deve confirmar que a obrigação acabou, apoiado por evidência técnica. Engenheiros podem provar impossibilidade, mas não deduzir mudança contratual do silêncio.