Como você exclui com segurança uma parte do código que parece nunca ter sido digitado?

125

Você encontrou algum código que parece supérfluo e o compilador não percebe isso. O que você faz para ter certeza (ou o mais próximo possível) de que excluir este código não causará regressão.

Duas ideias vêm à mente.

  1. "Simplesmente" use a dedução com base no fato de o código parecer ou não executar. No entanto, às vezes isso pode ser um trabalho complexo e demorado, um pouco arriscado (sua avaliação é propensa a erros), pois não há retorno substancial dos negócios.

  2. Coloque o registro nessa seção de código e veja com que frequência ele é inserido na prática. Depois de execuções suficientes, você deve ter certeza razoável de que remover o código é seguro.

Existe alguma ideia melhor ou algo parecido com uma abordagem canônica?

    
por Brad Thomas 07.03.2017 / 15:41
fonte

20 respostas

112

No meu mundo de fantasia perfeito onde eu tenho 100% de cobertura de teste de unidade, eu apenas a deleta, executo meus testes de unidade, e quando nenhum teste fica vermelho, eu confirmo isso.

Mas infelizmente eu tenho que acordar todas as manhãs e encarar a dura realidade onde muitos códigos não têm testes de unidade ou quando eles estão lá não podem ser confiáveis para realmente cobrir todos os casos possíveis. Então, eu consideraria o risco / recompensa e chegaria à conclusão de que simplesmente não vale a pena:

  • Recompensa: o código é um pouco mais fácil de manter no futuro.
  • Risco: Quebrar o código em algum caso de borda obscura que eu não estava pensando, causar um incidente quando eu menos esperar, e ser culpado por isso porque eu tive que satisfazer meu OCD de qualidade de código e fazer uma alteração sem valor de negócio perceptível para qualquer parte interessada que não seja um programador.
por 07.03.2017 / 15:52
fonte
83

Existem duas metades para este processo. O primeiro é confirmar que o código está realmente morto. A segunda é compreender os custos de estar errado e garantir que eles sejam adequadamente mitigados.

Muitas respostas aqui têm ótimas soluções para a primeira metade. Ferramentas como analisadores estáticos são ótimas para identificar código morto. grep pode ser seu amigo em alguns casos. Um passo incomum que geralmente faço é tentar identificar qual era o objetivo original do código. É muito mais fácil argumentar: “o X não é mais uma característica do nosso produto, e o segmento de código Y foi projetado para suportar o recurso X”, do que dizer “não vejo nenhuma finalidade para o segmento de código Y”.

A segunda metade é um passo fundamental para quebrar qualquer impasse sobre se você deve remover o código. Você precisa entender quais são as implicações de obter a resposta errada. Se as pessoas vão morrer se você errar a resposta, preste atenção! Talvez seja um bom momento para aceitar que o código se desenvolve com o tempo e, em vez disso, tente não escrever mais. Se as pessoas não morrerem, pergunte a si mesmo o quão tolerantes são seus usuários. Você pode enviar-lhes um hotfix se você quebrou alguma coisa e manter suas relações com o cliente? Você tem uma equipe de Q & A que paga para encontrar problemas como este? Esse tipo de pergunta é essencial para entender como você deve estar certo antes de apertar a tecla delete.

Nos comentários, rmun apontou um excelente fraseado do conceito de entender o propósito original do código antes de removê-lo. A citação agora é conhecida como Cerca de Chesterton . Embora seja muito grande para ser citado diretamente em um comentário, acho que merece ser citado corretamente aqui:

In the matter of reforming things, as distinct from deforming them, there is one plain and simple principle; a principle which will probably be called a paradox. There exists in such a case a certain institution or law; let us say, for the sake of simplicity, a fence or gate erected across a road. The more modern type of reformer goes gaily up to it and says, “I don’t see the use of this; let us clear it away.” To which the more intelligent type of reformer will do well to answer: “If you don’t see the use of it, I certainly won’t let you clear it away. Go away and think. Then, when you can come back and tell me that you do see the use of it, I may allow you to destroy it.

    
por 07.03.2017 / 17:42
fonte
43

Também tenho a tendência de grep para o nome da função / classe no código, o que pode fornecer alguns benefícios adicionais que um analisador de código pode não ter, como se o nome fosse mencionado em um comentário ou em um arquivo de documentação ou roteiro, por exemplo. Eu corro o grep nos arquivos na árvore de código-fonte e armazeno o resultado em um arquivo; geralmente o resultado fornece uma informação condensada: nome / caminho do arquivo, número da linha e a linha onde o nome é encontrado, que pode fornecer pistas sobre onde a função / classe é chamada ou mencionada sem qualquer significado semântico (em contraste com um analisador de código). ) e independentemente das extensões de arquivo. Definitivamente não é a solução definitiva, mas uma boa adição a uma análise.

    
por 07.03.2017 / 17:15
fonte
30
  • Identifique o código que parece morto (análise estática, etc.).
  • Adicione uma mensagem de log para todas as invocações do código supostamente inoperante. É fácil com funções / métodos; com membros estáticos como constantes é mais complicado. Às vezes, você pode simplesmente marcar o código como obsoleto e o tempo de execução registrará uma mensagem automaticamente. Deixe o código intacto.
    • Registra uma mensagem quando o módulo inativo é carregado: a maioria dos idiomas tem uma maneira de executar a inicialização estática no momento do carregamento, que pode ser armazenada em piggy-back.
    • Verifique se a sua mensagem de log inclui um rastreamento de pilha razoável, para que você entenda o que chamou o código supostamente inoperante.
  • Execute o código alterado em todos os seus conjuntos de testes. Suas suítes de teste também devem testar para momentos especiais, como cruzar uma meia-noite, um turno de um quarto, um turno de um ano, etc. Veja os registros, atualize sua compreensão do que está morto. Observe que os testes de unidade podem testar especificamente o código morto, enquanto nenhum outro teste de unidade e nenhum teste de integração o toca.
  • Execute o código alterado em produção por algumas semanas. Certifique-se de que todos os processos periódicos, como os trabalhos cronológicos ETL de uma vez por mês, sejam executados durante este período.
  • Veja os registros. Tudo o que foi registrado não está realmente morto. O fechamento transitivo do gráfico de chamada sobre o resto do código morto também é potencialmente não morto, mesmo que não tenha sido invocado. Analise isso. Talvez algumas ramificações estejam seguras (por exemplo, trabalhando com uma API de um serviço extinto), enquanto outras ainda não estão. Talvez todo um módulo / classe seja carregado apenas para uma constante definida, e você pode facilmente desabilitá-lo.
  • O que sobrar está inativo e pode ser removido.
por 07.03.2017 / 17:55
fonte
16

Além das respostas já mencionadas, você também pode remover iterativamente o código em várias versões.

Na versão inicial, você poderia fazer um aviso de descontinuidade com o bloco de códigos ainda em funcionamento. Na versão posterior, você pode remover o bloco de código, mas deixar uma mensagem de erro permitindo que os usuários tenham o recurso obsoleto e não estejam mais disponíveis. Na versão final, você removeria o bloco de códigos e todas as mensagens.

Isso pode ajudar a identificar funcionalidades imprevistas sem aviso aos usuários finais. Na melhor das hipóteses, o código realmente não faz nada e tudo o que acontece é que o código desnecessário é mantido em várias versões antes de ser removido.

    
por 08.03.2017 / 04:05
fonte
7

Muitas pessoas estão sugerindo que a coisa "segura" a fazer é deixar o código se você não puder provar que não está sendo usado.

Mas o código não é um ativo, é um risco.

A menos que você possa explicar por que é importante e apontar para seus testes, a alternativa "mais segura" pode muito bem ser excluí-la.

Se você ainda não tem certeza, pelo menos certifique-se de adicionar testes para exercitar o código zumbi.

    
por 08.03.2017 / 23:56
fonte
6

Você pode usar o recurso de alternar para alterar o caminho de execução do seu software para ignorar completamente o código em questão.

Dessa forma, você pode implantar com segurança sua alteração com o código não em uso e com a opção desativada. Se você notar algumas falhas graves relacionadas ao código, ative o botão novamente e investigue o caminho possível para ele.

Essa abordagem deve proporcionar confiança se você não perceber problemas durante um período de tempo prolongado, bem como a capacidade de ativá-la novamente sem uma implantação ativa. No entanto, uma maneira ainda melhor seria também aplicar log extra e cobertura de teste em torno da área em questão, o que fornecerá mais evidências se é usado ou não.

Leia mais sobre alternar aqui: link

    
por 07.03.2017 / 17:10
fonte
6

Análise estática, claro ... e o bom é que você não precisa de novas ferramentas; seu compilador tem todas as ferramentas de análise estática que você precisa.

Basta alterar o nome do método (por exemplo, alterar DoSomething para DoSomethingX ) e, em seguida, executar uma compilação.

Se a sua construção for bem-sucedida, o método, obviamente, não está sendo usado por nada. É seguro excluir.

Se a sua compilação falhar, isole a linha de código que a chama e verifique quais declarações if envolvem a chamada para determinar como ativá-la. Se você não encontrar nenhum caso de uso de dados possível que o acione, poderá excluí-lo com segurança.

Se você estiver realmente preocupado em excluí-lo, considere manter o código, mas marcando-o com um ObsoleteAttribute (ou o equivalente no seu idioma). Solte-o assim para uma versão, depois remova o código depois que nenhum problema for encontrado na natureza.

    
por 08.03.2017 / 05:29
fonte
4
  • Eu começaria a usar um analisador de código para verificar se este é um código morto
  • Eu começaria a verificar meus testes e a tentar alcançar o código. Se eu sou não é possível alcançar o código dentro de um caso de teste, pode ser código morto
  • Eu removeria o código e lançaria uma exceção. Para uma liberação, a exceção é ativada e, na segunda liberação, a exceção pode ser removida. Por segurança, coloque um sinalizador de emergência (em Java, uma propriedade do sistema) para poder ativar o código original, quando um cliente perceber a exceção. Portanto, a exceção pode ser desativada e o código original pode ser ativado em um ambiente de produção.
por 07.03.2017 / 15:54
fonte
3

Removendo o código inacessível

Em uma linguagem de tipo estaticamente estável, você deve sempre saber se o código está realmente acessível ou não: remova-o, compile, se não houver erro, ele não poderá ser acessado.

Infelizmente, nem todas as linguagens são estaticamente digitadas, e nem todas as linguagens estaticamente tipadas são baseadas em princípios. Coisas que podem dar errado incluem (1) reflexão e (2) sobrecarga sem princípios.

Se você usar uma linguagem dinâmica ou uma linguagem com reflexão suficientemente poderosa de que o fragmento de código sob exame poderia ser acessado em tempo de execução via reflexão, então você não pode confiar no compilador. Essas linguagens incluem Python, Ruby ou Java.

Se você usar um idioma com sobrecarga sem princípios, simplesmente remover uma sobrecarga poderia simplesmente mudar a resolução de sobrecarga para outra sobrecarga silenciosamente. Algumas dessas linguagens permitem que você programe um aviso / erro em tempo de compilação associado ao uso do código, caso contrário, você não pode confiar no compilador. Essas linguagens incluem Java (use @Deprecated ) ou C ++ (use [[deprecated]] ou = delete ).

Então, a menos que você tenha muita sorte de trabalhar com linguagens estritas (Rust vem à mente), você pode estar atirando no próprio pé confiando no compilador. E, infelizmente, as suítes de testes são geralmente incompletas, então não há muito mais ajuda.

Sugiro a próxima seção ...

Removendo código potencialmente não utilizado

Mais provavelmente, o código é realmente referenciado, mas você suspeita de que na prática os ramos de código que o referenciam nunca são utilizados.

Neste caso, não importa o idioma, o código é demonstravelmente acessível, e somente a instrumentação em tempo de execução pode ser usada.

No passado, usei com sucesso uma abordagem de três fases para remover esse código:

  1. Em cada ramo suspeito de NÃO ser levado, registre um aviso.
  2. Após um ciclo, lançar uma exceção / retornar um erro ao inserir o trecho de código específico.
  3. Após outro ciclo, exclua o código.

O que é um ciclo? É o ciclo de uso do código. Por exemplo, para uma aplicação financeira, esperaria um ciclo mensal curto (com salários pagos no final do mês) e um ciclo anual longo. Nesse caso, você deve aguardar pelo menos um ano para verificar se nenhum aviso é emitido para o inventário de final de ano que poderia usar caminhos de código que nunca seriam usados.

Espero que a maioria dos aplicativos tenha ciclos mais curtos.

Eu aconselho colocar um comentário TODO, com uma data, aconselhando sobre quando passar para a próxima etapa. E um lembrete no seu calendário.

    
por 08.03.2017 / 11:28
fonte
2

Remover código da produção é como limpar a casa. No momento em que você joga fora um objeto do sótão, sua esposa vai matá-lo no dia seguinte por ter jogado fora o presente de regresso a casa do terceiro sobrinho de sua bisavó de seu vizinho que morreu em 1923.

Sério, após análise superficial usando várias ferramentas que todos já mencionaram, e depois de usar a abordagem de descontinuidade escalonada já mencionada, tenha em mente que você pode sair da empresa quando a remoção real ocorrer. Deixar o código permanecer e ter sua execução registrada e garantir que todos os alertas de sua execução sejam definitivamente comunicados a você (ou a seus sucessores e designados) é essencial.

Se você não seguir essa abordagem, você provavelmente será morto por sua esposa. Se você mantiver o código (como todas as lembranças), então você pode descansar a sua consciência no fato de que a lei de Moore vem em seu socorro e o custo do espaço em disco usado pelo código que parece um "rock no meu sapato", reduz cada ano e você não corre o risco de se tornar o centro de várias fofocas de refrigeradores de água e olhares estranhos nos corredores.

PS: Para esclarecer em resposta a um comentário. A pergunta original é "Como você segura excluir ...", é claro, o Controle de Versão é assumido na minha resposta. Assim como cavar no lixo para encontrar o valor é assumido. Nenhum corpo com qualquer sentido joga fora o controle de código e versão deve ser parte do rigor de cada desenvolvedor.

O problema é sobre código que pode ser supérfluo. Nunca podemos saber que um pedaço de código é supérfluo, a menos que possamos garantir que 100% dos caminhos de execução não o alcancem. E supõe-se que este é um software suficientemente grande que esta garantia é quase impossível. E, a menos que eu tenha lido a pergunta errada, a única vez que todo esse segmento de conversação é relevante é para casos durante a produção, onde o código removido pode ser chamado e, portanto, temos um problema de tempo de execução / produção. Controle de versão não salva ninguém por trás devido a falhas de produção, então o comentário sobre "Controle de Versão" não é relevante para a questão ou o meu ponto original, ou seja, depreciar adequadamente em um período extremamente longo se você realmente precisar Não se preocupe, porque o código supérfluo adiciona relativamente um custo muito pequeno no inchaço do espaço em disco.

IMHO, o comentário é supérfluo e é um candidato para remoção.

    
por 08.03.2017 / 05:47
fonte
1

Talvez o compilador perceba isso, apenas não faz barulho.

Execute uma compilação com otimizações completas do compilador, voltadas para o tamanho. Em seguida, exclua o código suspeito e execute a construção novamente.

Compare os binários. Se eles forem idênticos, o compilador notou e silenciosamente excluiu o código. Você pode excluí-lo da fonte com segurança.

Se os binários são diferentes ... então é inconclusivo. Pode ser outra mudança. Alguns compiladores incluem até mesmo data e hora de compilação no binário (e talvez ele possa ser configurado!)

    
por 08.03.2017 / 10:46
fonte
1

Na verdade, recentemente me deparei com essa situação exata, com um método chamado "deleteFoo". Em nenhum lugar da base de código inteira havia aquela string que não fosse a declaração do método, mas eu sabiamente escrevi uma linha de registro no topo do método.

PHP:

public function deleteFoo()
{
    error_log("In deleteFoo()", 3, "/path/to/log");
}

Acontece que o código foi usado! Alguns métodos AJAX chamam "método: delete, elem = Foo" que é então concatenado e chamado com call_user_func_array () .

Em caso de dúvida, faça o login! Se você passou tempo suficiente sem ver o log preenchido, considere remover o código. Mas mesmo se você remover o código, deixe o log no lugar e um comentário com a data para tornar mais fácil encontrar o commit no Git para o cara que tem que mantê-lo!

    
por 08.03.2017 / 11:03
fonte
0

Use grep ou qualquer outra ferramenta disponível, você pode encontrar referências ao código. (Não originalmente minhas instruções / conselhos.)

Em seguida, decida se deseja arriscar remover o código ou se minha sugestão pode vir a ser usada:

Você pode modificar a função / bloco de código para escrever que foi usado em um arquivo de log. Se nenhuma mensagem desse tipo for "gravada" no arquivo de log, provavelmente será possível remover o código de log.

Dois problemas:

  1. Obtendo o registro de outros usuários. Talvez você possa definir um sinalizador global que trave o programa ao sair e peça ao usuário que envie o registro de erros.

  2. Rastreando o chamador. Em algumas linguagens de alto nível (por exemplo, Python), você pode obter facilmente um rastreamento. Mas nas linguagens compiladas você terá que salvar o endereço de retorno no log e forçar um dump principal na saída (ponto 1). Em C, isso deve ser bem simples: use um bloco condicional (por exemplo, #ifdef ... #endif) para usá-lo somente quando você souber que ele funcionará (e ter testado isso) e simplesmente leia o endereço de retorno da pilha (pode ou não exigir linguagem de montagem embutida).

Salvar os argumentos no arquivo de log pode ou não ser útil.

    
por 07.03.2017 / 17:37
fonte
0

Se você sabe o código que o rodeia, teste-o para ver se ele quebra. Essa é a maneira mais simples e econômica de refatorar uma parte do código em que você está trabalhando, e deve ser feito de qualquer maneira como uma questão de desenvolver qualquer mudança no código em que você está trabalhando.

Se, por outro lado, você estiver refatorando uma parte do código onde você não sabe o que ela faz, não a remova . Entenda-o primeiro, depois refatore-o se você determinar que ele é seguro (e somente se você puder determinar que a refatoração seria um uso adequado do seu tempo).

Agora, pode haver algumas circunstâncias (eu sei que já corri para isso) em que o código "morto" não está conectado a qualquer coisa . Tais como bibliotecas de código desenvolvidas por um contratante que nunca foram realmente implementadas em qualquer lugar porque se tornaram obsoletas imediatamente após o aplicativo ser entregue (por que sim, eu tenho um exemplo específico em mente, como você sabia?).

Eu, pessoalmente, acabei removendo todo esse código, mas é um risco enorme que não recomendo assumir o controle levemente - dependendo de quanto código essa alteração poderia potencialmente

Agora, tudo isso dito, provavelmente não vale o seu tempo ou sanidade para tentar remover códigos desse tipo. Não, a menos que esteja se tornando um problema sério com o seu aplicativo (por exemplo, não poder completar as verificações de segurança devido ao inchaço do aplicativo ... por que sim eu ainda tenho um exemplo em mente), então não o recomendaria a menos que tal situação em que o código realmente começou a apodrecer de maneira impactante.

Então, em suma - se você sabe o que o código faz, teste primeiro. Se você não fizer isso, você provavelmente pode deixá-lo sozinho. Se você sabe que não pode deixá-lo sozinho, dedique seu tempo para testar a mudança agressivamente antes de implementar a mudança.

    
por 09.03.2017 / 15:44
fonte
0

Minha abordagem, que sempre assumi ser o padrão da indústria neste caso, não foi mencionada até o momento:

Pegue um segundo par de olhos.

Existem diferentes tipos de "código não utilizado" e, em alguns casos, a exclusão é apropriada; em outros casos, você deve refatorá-la e, em outros casos, você corre o mais longe possível e nunca olha para trás. Você precisa descobrir qual deles é e, para isso, é melhor fazer um par com um segundo - experiente! - desenvolvedor, alguém que está muito familiarizado com a base de código. Isso reduz significativamente o risco de um erro desnecessário e permite que você faça as melhorias necessárias para manter a base de código em um estado de manutenção. E se você não fizer isso, em algum momento você fica sem desenvolvedores que estão muito familiarizados com o código base.

Eu vi a abordagem de registro também - para ser mais exato, eu vi o código de registro: ainda mais código morto que foi deixado lá por 10 anos. A abordagem de logging é bastante decente se você estiver falando sobre a remoção de recursos inteiros, mas não se você estiver apenas removendo um pequeno código morto.

    
por 11.03.2017 / 00:59
fonte
0

A resposta simples à sua pergunta é que você não pode excluir com segurança o código que não entende, o que inclui não entender como ele é chamado. Haverá algum risco.

Você terá que julgar a melhor forma de mitigar esse risco inevitável. Outros mencionaram o registro em log. Logging pode provar que é usado, mas não pode provar que não é. Como o maior problema com código morto é que ele é mantido, você pode conseguir adicionar um comentário dizendo que é suspeito de código morto e não fazer nenhuma manutenção nele.

Se o código estiver sob controle de origem, você sempre poderá descobrir quando ele foi adicionado e determinar como ele foi chamado.

Por fim, você entende, deixa para lá ou arrisca.

    
por 11.03.2017 / 15:02
fonte
0

Caso o desenvolvedor do código em questão ainda esteja disponível, analise a remoção do código com ele . Além disso, leia os comentários no código .

Você receberá a explicação adequada, por que esse código foi adicionado e para qual função ele serve. Ele pode ser facilmente compatível com o caso de uso específico ou você pode não ter conhecimento suficiente para julgar se realmente não é necessário. Em casos extremos, pode-se remover todas as declarações livres de um programa em C e não notar nada de errado (pode ser necessário alguns minutos para ficar sem memória).

É realmente uma cultura ruim quando o autor do código se senta ao seu lado, mas você não vê razão para falar porque tem certeza de que ele apenas escreve código ruim, e o seu é tudo tão perfeito. E, claro, ele apenas escreve palavras aleatórias nos comentários, sem necessidade de perder tempo lendo.

Uma das razões mais importantes para escrever um comentário no código é explicar por que ele foi implementado dessa maneira. Você pode discordar da solução, mas precisa levar o problema em consideração.

A discussão com o autor também pode revelar que o código é o remanescente histórico de algo removido ou nunca concluído, por isso é seguro excluir.

    
por 11.03.2017 / 19:01
fonte
-1
Eu concordo strongmente com o primeiro ponto de Markus Lausberg: O código deve definitivamente ser analisado com a ajuda de ferramentas. Os cérebros dos macacos não são confiáveis neste tipo de tarefa!

A maioria das linguagens de programação padrão pode ser usada em IDEs e em todos os IDE que valem a pena serem chamados que tenham uma característica de "localizar todos os usos" - que é exatamente a ferramenta certa para esse tipo de situação. (Ctrl + Shift + G no Eclipse por exemplo)

É claro: as ferramentas do analisador estático não são perfeitas. É preciso estar ciente de situações mais complicadas em que a decisão de que o método em questão está sendo chamado acontece apenas em tempo de execução e não antes (chamadas via Reflection, chamadas para funções "eval" em linguagens de script etc).

    
por 07.03.2017 / 17:25
fonte
-1

Duas possibilidades:

  1. você tem 99% de cobertura de teste com : exclua o código e conclua o processo.
  2. Você não tem cobertura de teste confiável: a resposta é adicionar um aviso de depreciação . Ou seja, você adiciona algum código a esse local que faz algo sensato (registra um aviso em algum lugar facilmente visível que não entra em conflito com o uso diário do seu aplicativo, por exemplo). Então deixe ferver por alguns meses / anos. Se você nunca encontrou alguma ocorrência, substitua a depreciação por algo que gera uma exceção. Deixe ficar por um tempo. Por fim, remova tudo completamente.
por 09.03.2017 / 17:36
fonte

Tags