Como você exclui com segurança um pedaço de código que parece nunca ter sido inserido?

125

Você encontrou um 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 a exclusão desse código não causará regressão.

Duas idéias vêm à mente.

  1. "Simplesmente" use dedução com base em se o código deve ou não ser executado. No entanto, às vezes, esse pode ser um trabalho complexo, demorado e levemente arriscado (sua avaliação está sujeita a erros), sem retorno comercial significativo.

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

Existem idéias melhores ou algo como uma abordagem canônica?

Brad Thomas
fonte
55
Pode ser útil examinar o histórico de controle de versão: Quando o "código morto" foi criado? Parecia morto quando foi criado? Outro código (que foi modificado ou excluído desde então) foi verificado no qual foi utilizado, ao mesmo tempo em que foi criado?
ChrisW
9
O compilador não pode ser confiável se houver alguma forma de metaprogramação, como a reflexão. Um grep adequado do diretório base é uma prática essencial. Também é comum ter vários aplicativos compartilhando código, portanto, grep na camada de diretório correta.
quer
16
Eu perguntaria - "Por que se preocupar em excluí-lo?". Se for usado em algum caso extremo chamado uma vez na lua azul, você quebrou as coisas. Se nunca for usado e você o deixar lá, o dano é que o arquivo executável é um pouco maior. A menos que isso seja para algum dispositivo incorporado, isso é um grande problema? Coloque um comentário e passe a usar seu tempo de forma produtiva.
mickeyf
40
@mickeyf O dano é que alguém precisa manter esse código. Isso significa que um desenvolvedor precisa entender o que faz. O desenvolvedor precisa modificá-lo se as coisas mudarem e o código ao seu redor agora precisar fazer algo diferente. Acredite em mim, quando você deixa o lixo por aí, é uma grande dor de cabeça para quem tenta entender as coisas mais tarde.
Jpmc26 9/17
9
@MichaelJ. Meu argumento é que não deveria estar lá em primeiro lugar. E se eu deixar, a situação é pior: agora algum outro desenvolvedor no final da linha também precisa investigar. Quanto mais tempo permanece, mais tempo é gasto para compreendê-lo. Esse é o custo do código supérfluo.
jpmc26

Respostas:

112

No meu mundo de fantasia perfeito, onde tenho 100% de cobertura de teste de unidade, eu a excluí, executava meus testes de unidade e, quando nenhum teste fica vermelho, eu o comprometo.

Mas, infelizmente, tenho que acordar todas as manhãs e enfrentar a dura realidade em que muitos códigos não têm testes de unidade ou quando estão lá não podem ser confiáveis ​​para realmente cobrir todos os possíveis casos extremos. 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: quebre o código em um caso obscuro em que não estava pensando, cause um incidente quando menos o espero e seja o culpado por ter que satisfazer o TOC com a qualidade do código e fazer uma alteração sem valor comercial perceptível a qualquer parte interessada que não seja ela própria um programador.
Philipp
fonte
249
Tendo observado o estado de grandes projetos de software nos quais os engenheiros, por dez a doze anos, seguiram principalmente a abordagem "não vale a pena" para remover o código supérfluo provável, não posso recomendar essa abordagem.
Njuffa
125
Além disso, os testes de unidade não informam se esse código é realmente usado na produção. O código pode ser perfeitamente testado, mas ainda não utilizado na produção, portanto supérfluo.
tipo protuberância
8
Na minha experiência, o estado de tais projetos nunca é devido a uma abordagem "não vale a pena" para corrigir o código após o fato, é sempre devido ao código incorreto sendo escrito em primeiro lugar. É um erro escrever código incorreto, mas também é um erro (um erro mais avançado) pensar que o código de limpeza sempre vale a pena.
Weyland Yutani
42
@Weyland Yutani Isso não corresponde à minha experiência. A maior parte do provável código supérfluo que eu vi foi suficientemente bem escrita e perfeitamente razoável, dadas as especificações de software, plataformas de hardware ou versões de sistema operacional existentes uma década antes, necessárias para solucionar bugs ou limitações na cadeia de ferramentas ou hardware disponível anteriormente, etc. .
njuffa
20
A resposta perde um dos benefícios mais críticos e importantes de avançar e fazer a mudança: aprender algo. Depois de aprender, você pode adicionar estrutura (comentários, testes) para ajudar os desenvolvedores de manutenção posteriormente. Optar por não mudar alguma coisa porque você não conhece as consequências é a programação do culto à carga, mesmo que essa escolha não seja excluir algo.
Kojiro # 8/17
84

Existem duas metades nesse processo. A primeira é confirmar que o código está realmente morto. O segundo é 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 ótimos para identificar código morto. greppode ser seu amigo em alguns casos. Um passo incomum que geralmente tomo é tentar identificar qual era o objetivo original do código. É muito mais fácil argumentar que "X não é mais um recurso 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 é uma etapa 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 do cruft se desenvolva com o tempo e, em vez disso, tente não escrever mais. Se as pessoas não vão morrer, pergunte a si mesmo como os seus usuários estão perdoando. Você pode enviar um hotfix a eles se você quebrou alguma coisa e mantém as relações com os clientes? Você tem uma equipe de perguntas e respostas paga para encontrar problemas como este? Esse tipo de pergunta é essencial para entender como você deve ter certeza antes de pressionar a tecla Delete.

Nos comentários, rmun apontou uma excelente redação do conceito de entender o objetivo 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:

Na questão de reformar as coisas, distinto de deformar, existe um princípio claro e simples; um princípio que provavelmente será chamado de paradoxo. Existe nesse caso uma determinada instituição ou lei; digamos, por uma questão de simplicidade, uma cerca ou portão erguido em uma estrada. O tipo mais moderno de reformador avança alegremente e diz: “Não vejo o uso disso; vamos esclarecer isso. ”Para o qual o tipo mais inteligente de reformador faria bem em responder:“ Se você não vê o uso dele, certamente não o deixarei esclarecer. Vá embora e pense. Então, quando você puder voltar e me dizer que vê o uso, posso permitir que você o destrua.

Cort Ammon
fonte
14
A Cerca de Chesterton assume que a descoberta ocorre apenas pelo pensamento e não oferece recurso - uma filosofia bastante grega-antiga. O método científico é mais moderno - crie um experimento controlado para determinar as consequências da remoção da estrutura. Certamente, eu não propriamente indiferente que esse experimento fosse realizado em produção. Mas, se não houver alternativa, e o custo do teste na produção for muito alto, bem, eu rapidamente deixaria um local de trabalho que não me ofereceu meios seguros para descobrir por que existe algum código - o risco pessoal em um emprego como esse é alto demais .
Kojiro # 8/17
15
@kojiro Isso é verdade. Eu gosto de pensar que Chesterton ficaria bem se eu, como um "reformador moderno", aparecesse e dissesse: "Não sei por que essa cerca está aqui, admito. Mas gostaria de pintá-la de vermelho para ver se isso me fornece qualquer informação nova ", Chesterton provavelmente ficaria feliz com isso.
Cort Ammon
41
O método de decodificação de relógio no modo de núcleo do sistema operacional VMS SYS $ ASCTIM contém uma linha de código que ajuda a corrigir a deriva sideral do equinócio vernal. Nenhum dos testes de unidade do DEC exercita esse código. Ele foi executado uma vez - corretamente - em 2000-02-28 às 23: 59: 59: 999. Ele não será executado novamente até o ano 2400. Por favor, não o exclua.
AI Breveleri
13
@AIBreveleri eu deveria esperar que há um comentário no código em si explicar isso, e não apenas aqui na Stackexchange: D
Kyle Strand
11
@KyleStrand Do que você está falando? Esta é a documentação. Você consegue pensar em um lugar melhor para colocar informações cruciais para que outras pessoas o encontrem além do StackExchange? ;-)
Cort Ammon
43

Também costumo grepusar o nome da função / classe no código, o que pode oferecer alguns benefícios adicionais que um analisador de código pode não oferecer, como se o nome fosse mencionado em um comentário ou em um arquivo de documentação ou em um script, por exemplo. Eu corro grep nos arquivos na árvore de origem e armazeno o resultado em um arquivo; geralmente o resultado fornece informações condensadas: nome / caminho do arquivo, número da linha e a linha onde o nome é encontrado, o 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.

piwi
fonte
11
Eu não sou um defensor negativo, mas e se a seção de código que você suspeitar nunca for executada não for uma função ou classe completa, mas uma seção de um método / função?
Tulains Córdova 07/03
9
Dependendo do idioma, isso nem sempre é confiável - alguns idiomas permitem que chamadas de método sejam feitas dinamicamente. Por exemplo, php:$object->$variableMethod($arg1, $arg2);
Dezza
9
@ TulainsCórdova Então essa provavelmente não é uma boa solução, obviamente. Como para qualquer solução em potencial, use-a se fizer sentido no contexto fornecido. Mas talvez grepo eg. A função que abrange a seção de código pode fornecer pistas sobre como a função é usada e, portanto, seu conteúdo?
achou
7
"como se o nome fosse mencionado em um comentário, arquivo de documentação ou script" - ou se alguém estiver usando reflexão para chamar o método (se estiver usando uma linguagem de alto nível / OO). Embora ainda não sejam necessariamente 100% precisos, como alguns idiomas suportam maneiras muito obtusas de encontrar e invocar métodos.
Aroth # 8/17
Isso é útil se você tiver acesso a todo o código possível que possa usar a função. No entanto, se o código fizer parte de uma biblioteca, como você saberá que outra coisa que você não pode pesquisar não será interrompida?
Kevin Shea
30
  • Identifique o código que parece morto (análise estática, etc.).
  • Adicione uma mensagem de log para cada chamada 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 para você. Deixe o código intacto.
    • Registre uma mensagem quando o módulo morto estiver carregado: a maioria dos idiomas possui uma maneira de executar a inicialização estática no tempo de carregamento, que pode ser suportada por piggy.
    • Certifique-se de que sua mensagem de log inclua um rastreamento de pilha razoável, para que você entenda o que chamou de código supostamente inoperante.
  • Execute o código alterado em todos os seus conjuntos de testes. Suas suítes de teste também devem ser testadas para horários especiais, como atravessar meia-noite, um turno de um quarto, uma virada de ano, etc. Veja os logs, 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 de integração o toca.
  • Execute o código alterado em produção por algumas semanas. Verifique se todos os processos periódicos, como os trabalhos cron ETL uma vez por mês, são executados durante esse período.
  • Olhe para os logs. Tudo o que foi registrado não está realmente morto. O fechamento transitivo do gráfico de chamada sobre o restante do código morto também pode não estar morto, mesmo que não tenha sido invocado. Analise isso. Talvez algumas filiais estejam mortas com segurança (por exemplo, trabalhando com uma API de um serviço agora extinto), enquanto outras ainda não. Talvez um módulo / classe inteiro seja carregado apenas para uma constante definida nele, e você pode desutilizá-lo facilmente.
  • O que resta é morto com segurança e pode ser removido.
9000
fonte
Existe uma primeira vez para tudo, portanto, apenas porque o código não foi executado antes não significa que ele nunca será executado, principalmente se houver um caminho de execução para ele. Dito isto, decidir remover algo que pode ser executado é um processo diferente de determinar se ele pode ser executado.
7
@nocomprende exatamente o que acontece se esse código é usado apenas para uma função de final de ano e você executa seu teste entre fevereiro e novembro.
Erik
1
@ 9000 Eu concordo em teoria, mas os sistemas legados estão repletos de dependências ocultas que podem frustrar esse tipo de teste. Para que seu exemplo de teste funcione, você precisa saber que existe algum tipo de acoplamento temporal. Se o acoplamento temporal for externo ao código que você está testando, você não o verá no código e saberá que o acoplamento temporal é um fator. Por exemplo, o código do silo A está instalado no GAC . Anos atrás, o silo B pediu ao silo A para adicionar um caminho de código para um relatório que eles precisavam executar nos dados do silo A. cont.
Erik
1
@ 9000 cont. Agora, o código do silo B tem um acoplamento temporal a um caminho de código "morto" no código do silo A que não é aparente apenas olhando para o código do silo A. Como você faria o teste de unidade para esse acoplamento temporal, uma vez que o acoplamento temporal está apenas no código do silo B sem saber falar com o silo B? Mesmo assim, o tempo não é um fator no seu código (silo A); então, como seria um teste temporal?
Erik
2
Alguém se lembra do pânico do Y2k ? Algum código estava certo em 2000, porque estava errado para 2100! Tivemos um problema em potencial que poderia ocorrer uma vez em 100 anos ou mesmo uma vez em 400 anos.
21917 Steve Barnes
16

Além das respostas existentes 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 descontinuação com o bloco de código ainda funcionando. Na versão seguinte, você pode remover o bloco de código, mas deixar uma mensagem de erro informando aos usuários que o recurso está obsoleto e não está mais disponível. Na versão final, você removeria o bloco de código e qualquer mensagem.

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.

user3196468
fonte
7
+1. Eu já vi tantos bugs e problemas voltados para o cliente que poderiam ter sido evitados se os desenvolvedores (ou seu gerenciamento) estivessem dispostos a dividir um projeto em duas ou três fases seguras, em vez de tentar colocar tudo de uma vez para encontrar um prazo arbitrário antes de passar para o próximo projeto.
Ruakh
Isso responde à pergunta feita no título, mas se você me perguntar, a pergunta tem o título errado. O título pergunta como excluir o código, mas o corpo é sobre como identificar se o código é seguro para excluir em primeiro lugar. Você definitivamente responde à primeira, mas não sei ao certo o que o OP estava realmente perguntando.
underscore_d
7

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

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

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 tiver certeza, pelo menos, certifique-se de adicionar testes para exercitar o código zumbi.

Adrian McCarthy
fonte
Etapa 1: exclua todo o código. Etapa 2: coloque de volta o que é necessário. Etapa 3: repita.
1
@ Adrian Posso chamar sua atenção para a Knight Capital? pt.wikipedia.org/wiki/… - caso queira reforçar seu argumento com uma referência a ele. Eles basicamente implodiram porque algum código antigo voltou à vida e gentilmente perdeu quase US $ 500 milhões para eles. A investigação que se seguiu se concentrou em seus procedimentos de liberação, mas o principal problema foi "a funcionalidade inoperante não foi excluída".
SusanW
6

Você pode usar as alternâncias de recursos 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 suas alterações com o código não em uso e desativar. Se você notar algumas falhas importantes relacionadas ao código, ative a alternância novamente e investigue o caminho possível para ele.

Essa abordagem deve fornecer confiança se você não encontrar problemas por um período prolongado, bem como a capacidade de reativá-la ao vivo sem uma implantação. No entanto, uma maneira ainda melhor seria também aplicar extra log e cobertura de teste na área em questão, o que fornecerá mais evidências, quer sejam usadas ou não.

Leia mais sobre alternâncias aqui: https://martinfowler.com/articles/feature-toggles.html

Faixa
fonte
6

Análise estática, é claro ... e o bom é que você não precisa de nenhuma ferramenta nova; seu compilador possui todas as ferramentas de análise estática necessárias.

Apenas mude o nome do método (por exemplo, mude DoSomethingpara DoSomethingX) e execute uma compilação.

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

Se sua compilação falhar, isole a linha de código que a chama e verifique quais ifinstruções cercam a chamada para determinar como acioná-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 marcá-lo com um ObsoleteAttribute (ou o equivalente no seu idioma). Libere-o assim para uma versão e remova o código depois que nenhum problema for encontrado.

John Wu
fonte
8
Este é um bom conselho para linguagens de programação que não possuem ligação dinâmica / tardia completa . No entanto, para aqueles que têm, é mais complicado. E, é claro - o código pode estar usando o método, mesmo que nunca seja usado na produção.
eis
A pergunta pressupõe a resolução do símbolo em tempo de compilação ( então você encontrou algum código que parece supérfluo. O compilador não percebe isso. ). E até a função ou objetos com limite de atraso são tipicamente definidos em dois locais, por exemplo, um arquivo de interface ou mapa, portanto a construção ainda falha.
John Wu
1
Hmm ... estou intrigado, não acho que o último que você diz seja verdade. Não é para javascript vanilla (não pré-compilado), ruby, python ou smalltalk, por exemplo, certo? Se você se referir a coisas que não existem, elas erro apenas em tempo de execução.
eis
Talvez discordemos do que significa "ligação", o que para mim indica a resolução de uma representação lógica para uma representação física, por exemplo, um símbolo para um endereço. As funções Javascript são armazenadas como pares de tag / valor no objeto que contém, portanto, qualquer noção de ligação parece não ser sequencial.
John Wu
1
Sim, nós realmente discordamos. O termo tem um significado diferente em idiomas que não possuem ligação tardia completa (como expliquei no primeiro comentário). Quando você vincula métodos em tempo de execução - em idiomas que podem adicionar e remover métodos em tempo real - em tempo real - você está usando a ligação tardia da maneira que eu entendo que isso deve ser feito. Isso é verdade nas linguagens de estilo ruby ​​/ python / smalltalk, e não há arquivos de mapa separados. Como especialmente ruby ​​e python são amplamente utilizados, discordo da sua noção do que "tipicamente" significaria.
eis
4
  • Eu começaria a usar um analisador de código para verificar se esse é um código morto
  • Eu começaria a verificar meus testes e tentar acessar o código. Se não conseguir acessar o código em um caso de teste, pode ser um código morto
  • Eu removeria o código e lançaria uma exceção. Para uma versão, a exceção é ativada e, na segunda, a exceção pode ser removida. Para estar seguro, 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.
Markus Lausberg
fonte
6
-1. Não é absolutamente recomendável fazer essas alterações no código produtivo, bandeira de segurança ou não. Somente quando se tem certeza de que o código em questão não é usado, ele deve ser removido no código produtivo. Esse tipo de confusão é exatamente por que sempre deve haver diferentes ambientes produtivos e de teste.
Johannes Hahn
3
Isso funcionará na fantasia quando a cobertura de teste for quase 90%, mas e se não, e você tiver um projeto com 100.000 linhas de código. É por isso que eu descrevi para tentar testar o código e se nenhum teste é capaz de alcançá-lo ... pode estar morto.
Markus Lausberg
IMHO Se você não tiver certeza de remover completamente o código - você não deve lançar exceções na produção - o registro sugerido pelo OP é uma alternativa muito melhor, obtém o mesmo e você pode obter diagnósticos em vez de depender de clientes reclamando
Sebi
@JohannesHahn Acho que você quer dizer "código de produção", que é um código executado em um ambiente de produção.
Jpmc26 9/17
@ jpmc26 Sim, é claro.
Johannes Hahn
3

Removendo código inacessível

Em uma linguagem com princípios estatísticos, você sempre deve saber se o código é realmente acessível ou não: remova-o, compile-o, se não houver erro, ele não foi alcançável.

Infelizmente, nem todos os idiomas são estaticamente tipificados e nem todos os idiomas estaticamente tipificados são baseados em princípios. As 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 para que o código sob análise possa ser acessado em tempo de execução via reflexão, não será possível confiar no compilador. Essas linguagens incluem Python, Ruby ou Java.

Se você usar um idioma com sobrecarga sem princípios, a simples remoção de uma sobrecarga pode simplesmente mudar a resolução de sobrecarga para outra sobrecarga silenciosamente. Alguns idiomas permitem programar 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]]or = delete).

Portanto, a menos que você tenha muita sorte de trabalhar com linguagens estritas (Rust vem à mente), você pode realmente estar se atirando no pé confiando no compilador. E, infelizmente, os conjuntos de testes geralmente são incompletos, portanto também não ajudam muito mais.

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


Removendo código potencialmente não utilizado

Mais provavelmente, o código é realmente referenciado, no entanto, você suspeita que, na prática, as ramificações do código que fazem referência a ele nunca sejam obtidas.

Nesse caso, independentemente do idioma, o código é demonstrável alcançável e apenas a instrumentação em tempo de execução pode ser usada.

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

  1. Em cada filial suspeita de NÃO ser tomada, registre um aviso.
  2. Após um ciclo, lance uma exceção / retorne um erro ao inserir um pedaço 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, eu esperaria um curto ciclo mensal (com salários sendo pagos no final do mês) e um longo ciclo anual. Nesse caso, é necessário aguardar pelo menos um ano para verificar se nenhum aviso foi emitido para o inventário de final de ano poderia usar caminhos de código que, de outra forma, nunca seriam usados.

Felizmente, a maioria dos aplicativos tem ciclos mais curtos.

Eu aconselho a colocar um comentário TODO, com uma data, informando quando avançar para a próxima etapa. E um lembrete no seu calendário.

Matthieu M.
fonte
1
Na verdade, em C ++, você pode marcar sua sobrecarga suspeita [[deprecated]]para identificar sites que chamam essa versão; então você pode inspecioná-los para ver se o comportamento deles mudará. Como alternativa, defina sua sobrecarga como = delete- nesse caso, você precisará converter explicitamente argumentos para usar uma das outras versões.
precisa
@TobySpeight: Isso é verdade, e uma boa alternativa também. Deixe-me editar isso.
Matthieu M.
"Doutor, dói quando eu faço isso." “Bem , não faça isso! ” Não use abordagens que tornem o código incontrolável.
2

Remover o código da produção é como limpar a casa. No momento em que você joga fora um objeto do sótão, sua esposa o mata no dia seguinte por jogar fora o presente de boas-vindas 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 depreciação por etapas já mencionada, lembre-se de que você pode ter saído da empresa quando a remoção real ocorrer. Deixar o código permanecer e ter sua execução registrada e garantir que quaisquer alertas de sua execução sejam definitivamente comunicados a você (ou a seus sucessores e designados) são essenciais.

Se você não seguir essa abordagem, provavelmente será morto por sua esposa. Se você mantiver o código (como toda lembrança), poderá descansar sua consciência no fato de que a lei de Moore vem em seu socorro e o custo do espaço em disco inútil usado pelo código que parece uma "pedra no meu sapato" reduz todos os ano e você não corre o risco de se tornar o centro de várias fofocas e olhares estranhos nos corredores.

PS: Para esclarecer em resposta a um comentário .. A pergunta original é "Como você exclui com segurança ..", é claro, o Controle de Versão é assumido na minha resposta. Assim como cavar o lixo para encontrar o valioso, é assumido. Nenhum organismo com qualquer sentido joga fora o código e o controle de versão deve fazer parte do rigor de todos os desenvolvedores.

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 seja um software suficientemente grande para que essa garantia seja quase impossível. E, a menos que eu leia a pergunta incorretamente, a única vez que todo esse segmento de conversa é relevante é nos casos durante a produção em que o código removido pode ser chamado e, portanto, temos um problema de tempo de execução / produção. O controle de versão não salva ninguém por trás de falhas de produção; portanto, o comentário sobre "Controle de versão" não é relevante para a pergunta ou o ponto original, ou seja, é preterido adequadamente por um período de tempo extremamente longo se você realmente precisar, caso contrário, não

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

LMSingh
fonte
12
Se eu tivesse o equivalente ao controle de versão , a objeção de minha esposa será facilmente revertida. O mesmo acontece com a exclusão do código: não é uma ofensa fatal. O repositório nunca é jogado fora.
JDługosz 8/03
Eu pensei que a programação orientada a objetos deveria restringir o escopo no qual um método poderia ser chamado. Portanto, deve levar a um código que seja mais facilmente compreendido e gerenciado, certo? Caso contrário, por que estamos fazendo dessa maneira?
@nocomprende Em nenhum lugar deste tópico é indicado que a discussão seja limitada apenas ao design / idiomas do OOP. Não sei por que você acha isso relevante.
underscore_d
1

Talvez o compilador faz notar que, ele simplesmente não fazer um 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 compilação novamente.

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

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

Emilio M Bumachar
fonte
1
Nem todos os idiomas são compilados, nem todos os compiladores têm otimização e nem todas as otimizações são iguais; muitos não removem código não invocado apenas no caso de esse código estar presente por algum motivo. Se o código estiver em uma biblioteca compartilhada / DLL, ele não será excluído.
9788 Steve Barnes
1
@SteveBarnes Bem, sim, se você não está fazendo LTO e vinculando tudo estático, está com alguma dor. Isso é um dado.
Navin
1

Na verdade, recentemente me deparei com essa situação exata, com um método chamado "deleteFoo". Em nenhum lugar de toda a base de código foi encontrada essa sequência além da declaração do método, mas eu sabiamente escrevi uma linha de log na parte superior 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 "method: delete, elem = Foo", que é concatenado e chamado com call_user_func_array () .

Em caso de dúvida, faça log! Se você passou um tempo suficiente sem ver o log preenchido, considere remover o código. Mas mesmo que você remova o código, deixe o log no local e um comentário com a data para facilitar a localização do commit no Git para quem precisa mantê-lo!

dotancohen
fonte
Existem bibliotecas para PHP e Perl chamadas Tombstone (php: github.com/scheb/tombstone ) que podem ser úteis aqui.
Alister Bulman
0

Use grepou quaisquer ferramentas disponíveis primeiro, você pode encontrar referências ao código. (Originalmente não é minha instrução / conselho.)

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

Você pode modificar a função / bloco de código para escrever que ele foi usado em um arquivo de log. Se nenhuma dessas mensagens for "sempre" registrada no arquivo de log, provavelmente você poderá remover o código de log.

Dois problemas:

  1. Obtendo o log de outros usuários. Talvez você possa definir um sinalizador global que trava o programa na saída e solicita ao usuário que envie o log de erros.

  2. Rastreando o chamador. Em algumas linguagens de alto nível (por exemplo, Python), você pode facilmente obter um rastreio. Mas em idiomas compilados, você precisará salvar o endereço de retorno no log e forçar um dump principal na saída (ponto 1).
    Em C, isso deve ser bastante simples: use um bloco condicional (por exemplo, #ifdef ... #endif) para usá-lo apenas quando você souber que funcionará (e tiver testado isso) e simplesmente leia o endereço de retorno da pilha (pode ou não exigir linguagem assembly em linha).

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

Oskar Skog
fonte
0

Se você souber qual é o código ao redor, teste-o para ver se ele quebra. Essa é a maneira mais simples e econômica de refatorar um pedaço de código no qual você está trabalhando e deve ser feito de qualquer maneira, como uma questão de desenvolver qualquer alteração no código em que você está trabalhando.

Se, por outro lado, você estiver refatorando um pedaço de código em que não sabe o que ele faz, não o remova . Entenda-o primeiro e depois refatorá-lo se você determinar que é 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 me deparei com isso) em que o código que está 'morto' não está realmente conectado a nada . Como bibliotecas de código desenvolvidas por um contratado que nunca foram implementadas em nenhum lugar porque ficaram obsoletas imediatamente após a entrega do aplicativo (por que sim, tenho um exemplo específico em mente, como você sabia?).

Pessoalmente, acabei removendo todo esse código, mas é um risco enorme que eu não recomendo assumir levianamente - dependendo de quanto código essa alteração possa potencialmente impactar (porque sempre há o potencial de que você tenha esquecido alguma coisa). deve ser extraordinariamente cuidadoso e executar testes de unidade agressivos para determinar se a remoção desse código antigo antigo quebrará seu aplicativo.

Agora, tudo isso dito, provavelmente não vale a pena gastar tempo ou sanidade mental para tentar remover um código como esse. A menos que esteja se tornando um problema sério com seu aplicativo (por exemplo, não sendo possível concluir as verificações de segurança por causa do inchaço do aplicativo ... por que sim, eu ainda tenho um exemplo em mente), não recomendaria, a menos que você chegue uma situação em que o código realmente começou a apodrecer de maneira impactante.

Então, resumindo - se você souber o que o código faz, teste-o primeiro. Se não o fizer, provavelmente poderá deixá-lo em paz. Se você sabe que não pode deixar para lá, dedique seu tempo para testar a mudança de forma agressiva antes de implementá-la.

Zibbobz
fonte
0

Minha abordagem, que eu sempre assumi como padrão do setor nesse caso, estranhamente não foi mencionada até agora:

Obtenha 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á-lo; em outros casos, você foge o mais longe possível e nunca olha para trás. Você precisa descobrir qual é e, para isso, é melhor emparelhar com um segundo experiente! - desenvolvedor, alguém familiarizado com a base de código. Isso reduz significativamente o risco de erro desnecessário e permite que você faça as melhorias necessárias para manter a base de código em um estado sustentável. E se você não fizer isso, em algum momento você ficará sem desenvolvedores familiarizados com a base de código.

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

Peter
fonte
0

A resposta simples para 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 log. É claro que o log pode provar que é usado, mas não pode provar que não é. Como o maior problema com o código morto é que ele é mantido, você pode adicionar um comentário dizendo que se suspeita 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 foi adicionado e determinar como foi chamado na época.

Por fim, você entende, deixa em paz ou se arrisca.

jmoreno
fonte
0

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

Você obterá a explicação adequada, por que esse código foi adicionado e para qual função ele serve. Pode ser facilmente suporte para o caso de uso específico, ou talvez você não tenha experiência suficiente para avaliar se realmente não é necessário. Em casos extremos, pode-se remover todas as instruções livres de um programa C e deixar de notar algo errado (pode levar alguns minutos para ficar sem memória).

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

Um dos motivos 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, portanto, é seguro excluir.

h22
fonte
-1

Concordo plenamente 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 ​​com esse tipo de tarefa !!

A maioria das linguagens de programação padrão pode ser usada nos IDEs e todo IDE que vale a pena ser chamado que possui um recurso "encontrar para todos os usos", que é exatamente a ferramenta certa para esse tipo de situação. (Ctrl + Shift + G no Eclipse, por exemplo)

Obviamente: as ferramentas de análise estática não são perfeitas. É preciso estar ciente de situações mais complicadas nas quais a decisão de que o método em questão está sendo chamado ocorre apenas em tempo de execução e não mais cedo (chamadas via Reflection, chamadas para funções "eval" em linguagens de script etc.).

Johannes Hahn
fonte
5
Talvez os cérebros dos macacos não devam escrever código? " Intocado por mãos humanas " costumava ser uma frase de marketing. Eu sempre imaginei as mãos de gorilas fazendo o trabalho.
3
(Cérebros Ape escreveu as ferramentas também) (Shhh Você vai arruinar a história!)
1
Eu acho que vocês dois estão perdendo o meu argumento. Eu disse claramente que não há garantia de que a análise de código estático encontre todas as invocações de um método ou classe. É, como diz na lata, uma análise estática. Não se pode esperar que se encontre invocações dinâmicas por meio de reflexão, eval () etc. Meu argumento é que a análise estática de código deve ser considerada um pré-requisito necessário para excluir um pedaço de código. E quanto maior o projeto é menos provável se torna que um cérebro humano é ainda capaz de fazer essa análise em tudo e muito menos fazê-lo corretamente ...
Johannes Hahn
1
... a análise de código estático é um trabalho repetitivo e tedioso, que é melhor realizado por um computador. Exatamente o tipo de tarefa que pode ser executada com segurança por computadores, mas é notoriamente não confiável quando executada por um cérebro de macaco. Este último deve ser usado apenas para coisas que um computador não pode fazer como encontrar aquelas reflexões complicadas e chamadas de avaliação.
Johannes Hahn
1
Mais uma vez, perdendo o objetivo. Mesmo que as ferramentas não sejam perfeitas porque foram escritas por cérebros dos macacos (e eu nem tenho certeza se devo admitir isso! Computar hierarquias de chamadas estáticas não é tão difícil), elas ainda são muito superiores à execução manual de tarefas. Esta não é uma situação do tipo tudo ou nada. Uma ferramenta com 99% de confiança ainda é preferível a um cérebro que talvez comece com 99%, mas cai rapidamente para taxas muito mais baixas após as primeiras mil linhas de código.
Johannes Hahn
-1

Duas possibilidades:

  1. Você tem 99% + cobertura de teste : exclua o código e termine com ele.
  2. Você não tem cobertura de teste confiável: a resposta é adicionar um aviso de descontinuação . Ou seja, você adiciona algum código a esse lugar que faz algo sadio (registre um aviso em algum lugar facilmente visível que não entre em conflito com o uso diário do seu aplicativo, por exemplo). Deixe ferver por alguns meses / anos. Se você nunca encontrou nenhuma ocorrência, substitua a depreciação por algo que gere uma exceção. Deixe isso por um tempo. Por fim, remova tudo completamente.
AnoE
fonte
2
isso não parece oferecer nada substancial sobre os pontos apresentados e explicados nas 17 respostas anteriores. Ambos aviso cobertura e depreciação foram discutidos por lá já
mosquito
@gnat, você está certo. Quando digitalizei as respostas pela primeira vez, por alguma razão, pensei ter visto várias que no topo não chegaram a esses dois pontos de maneira sucinta e sucinta. No meio das respostas, há algumas assim.
AnoE