Come si elimina in modo sicuro un pezzo di codice che sembra non sia mai stato inserito?


125

Hai trovato del codice che sembra superfluo e il compilatore non se ne accorge. Cosa fai per essere sicuro (o il più vicino possibile) che l'eliminazione di questo codice non causerà la regressione.

Mi vengono in mente due idee.

  1. "Semplicemente" usa la deduzione in base al fatto che il codice sembri o meno debba essere eseguito. Tuttavia, a volte questo può essere un lavoro complesso, che richiede tempo, leggermente rischioso (la tua valutazione è soggetta a errori) per nessun ritorno di affari sostanziale.

  2. Inserisci la registrazione in quella sezione di codice e vedi quanto spesso viene inserito nella pratica. Dopo un numero sufficiente di esecuzioni, dovresti avere una ragionevole sicurezza nel rimuovere il codice è sicuro.

Ci sono idee migliori o qualcosa come un approccio canonico?


55
Può essere utile esaminare la cronologia dei controlli di versione: quando è stato creato il "codice morto"? Sembrava morto quando è stato creato? È stato archiviato un altro codice (che è stato successivamente modificato o eliminato) che lo ha utilizzato, contemporaneamente alla sua creazione?
ChrisW,

9
Non ci si può fidare del compilatore se è in gioco qualsiasi forma di meta-programmazione come la riflessione. Un grep corretto della directory di base è una pratica essenziale. È anche comune avere più applicazioni che condividono il codice, quindi assicurati di grep al livello di directory corretto.
Adam Caviness,

16
Vorrei chiedere: "Perché preoccuparsi di eliminarlo?". Se viene utilizzato in un caso limite che viene chiamato una volta in una luna blu, hai rotto le cose. Se non viene mai utilizzato e lo lasci lì, il danno è che il file eseguibile è un po 'più grande. A meno che non sia per alcuni dispositivi embedded, questo è un grosso problema? Inserisci un commento su di esso e passa all'utilizzo produttivo del tuo tempo.
mickeyf

40
@mickeyf Il danno è che qualcuno deve mantenere quel codice. Ciò significa che uno sviluppatore deve capire cosa fa. Lo sviluppatore deve modificarlo se le cose cambiano e il codice che lo circonda ora deve fare qualcosa di diverso. Credetemi, quando lasciate la Croft in giro, è un grosso mal di testa per chiunque cerchi di capire le cose in seguito.
jpmc26

9
@MichaelJ. Il mio punto è che in primo luogo non avrebbe dovuto essere lì. E se lo lascio, la situazione è peggiore: ora anche qualche altro sviluppatore deve indagare. Più a lungo rimane, più tempo viene speso per capirlo. Questo è il costo del codice superfluo.
jpmc26

Risposte:


112

Nel mio perfetto mondo fantastico in cui ho una copertura del 100% per i test unitari, lo eliminerei, eseguivo i test unitari e quando nessun test diventa rosso, lo commetto.

Ma sfortunatamente devo svegliarmi ogni mattina e affrontare la dura realtà in cui un sacco di codice non ha test unitari o quando sono lì non ci si può fidare di coprire davvero ogni possibile caso limite. Quindi prenderei in considerazione il rischio / rendimento e giungerei alla conclusione che semplicemente non ne vale la pena:

  • Ricompensa: il codice è un po 'più facile da mantenere in futuro.
  • Rischio: rompere il codice in un caso oscuro al limite a cui non stavo pensando, causare un incidente quando meno me lo aspetto ed essere responsabile perché ho dovuto soddisfare il mio disturbo ossessivo compulsivo di qualità del codice e apportare una modifica senza alcun valore aziendale percepibile a tutti gli stakeholder che non sono essi stessi programmatori.

249
Avendo osservato lo stato dei grandi progetti software in cui gli ingegneri, da dieci a dodici anni, avevano seguito per lo più l'approccio "non ne vale la pena" per rimuovere il probabile codice superfluo, non posso raccomandare questo approccio.
njuffa,

125
Inoltre, i test unitari non dicono nulla sull'effettivo utilizzo di questo codice in produzione. Il codice potrebbe essere perfettamente testato, ma non ancora utilizzato in produzione, quindi superfluo.
Knub

8
Nella mia esperienza, lo stato di tali progetti non è mai dovuto a un approccio "non degno" per correggere il codice dopo il fatto, è sempre a causa della cattiva scrittura del codice in primo luogo. Scrivere un codice errato è un errore, ma è anche un errore (un errore più avanzato) pensare che ne valga sempre la pena.
Weyland Yutani,

42
@Weyland Yutani Non corrisponde affatto alla mia esperienza. La maggior parte del probabile codice superfluo che ho visto era sufficientemente ben scritto e perfettamente ragionevole dato le specifiche del software, le piattaforme hardware o le versioni del sistema operativo esistenti un decennio prima, necessarie per aggirare bug o limitazioni nella catena di strumenti o hardware disponibili prima, ecc. .
njuffa

20
La risposta manca di uno dei vantaggi più importanti e importanti di andare avanti e apportare il cambiamento: imparare qualcosa. Dopo averlo appreso, è possibile aggiungere una struttura (commenti, test) per aiutare gli sviluppatori della manutenzione in un secondo momento. Scegliere di non cambiare qualcosa perché non si conoscono le conseguenze è la programmazione di culto del carico, anche se quella scelta non è quella di eliminare qualcosa.
Kojiro,

84

Ci sono due metà di questo processo. Il primo sta confermando che il codice è effettivamente morto. Il secondo è comprendere i costi dell'errore e assicurarsi che siano adeguatamente mitigati.

Molte risposte qui offrono grandi soluzioni alla prima metà. Strumenti come gli analizzatori statici sono ottimi per identificare il codice morto. greppuò essere tuo amico in alcuni casi. Un passo insolito che faccio spesso è cercare di identificare quale fosse lo scopo originale del codice. È molto più facile sostenere che "X non è più una caratteristica del nostro prodotto e il segmento di codice Y è stato progettato per supportare la funzione X" piuttosto che dire "Non vedo alcuno scopo per il segmento di codice Y".

La seconda metà è un passaggio chiave per interrompere qualsiasi blocco della griglia se è necessario rimuovere il codice. Devi capire quali sono le implicazioni di sbagliare la risposta. Se le persone moriranno se sbagli la risposta, presta attenzione! Forse è un buon momento per accettare che il codice cruft si sviluppi nel tempo e invece cerchi di non scrivere più cruft da solo. Se le persone non moriranno, chiediti quanto sono indulgenti i tuoi utenti. Puoi inviare loro un aggiornamento rapido se hai rotto qualcosa e mantenuto le relazioni con i clienti? Hai un team di domande e risposte pagato per trovare problemi come questo? Questo tipo di domande sono essenziali per capire quanto devi essere sicuro prima di premere il tasto Canc.

Nei commenti, rmun ha sottolineato un'eccellente formulazione del concetto di comprensione dello scopo originale del codice prima di rimuoverlo. La citazione è ora conosciuta come il recinto di Chesterton . Sebbene sia troppo grande per essere citato direttamente in un commento, penso che meriti di essere citato correttamente qui:

In materia di riforma delle cose, distinto dal deformarle, esiste un principio chiaro e semplice; un principio che probabilmente verrà chiamato un paradosso. Esiste in tal caso una certa istituzione o legge; diciamo, per semplicità, una recinzione o un cancello eretto attraverso una strada. Il tipo più moderno di riformatore si getta allegramente e dice: “Non vedo l'uso di questo; cerchiamo di cancellarlo ”. A cui il tipo più intelligente di riformatore farà bene a rispondere:“ Se non ne vedi l'uso, non lo lascerò certamente. Vattene e pensa. Quindi, quando puoi tornare e dirmi che ne vedi l'uso, potrei permetterti di distruggerlo.


14
Il Recinto di Chesterton presume che la scoperta arrivi puramente attraverso il pensiero e non offre ricorso - una filosofia piuttosto antica-greca. Il metodo scientifico è più moderno: escogitare un esperimento controllato per determinare le conseguenze della rimozione della struttura. A dire il vero, non proporrei con disinvoltura questo esperimento da fare in produzione. Ma se non ci sono alternative e il costo della sperimentazione in produzione è molto alto, beh, preferirei lasciare rapidamente un posto di lavoro che non mi offriva mezzi sicuri per imparare perché esiste un codice: il rischio personale in un simile lavoro è troppo alto .
Kojiro,

15
@kojiro È vero. Mi piace pensare che Chesterton starebbe bene se io, come "riformatore moderno", venissi e dicessi "Non so perché questa recinzione è qui, lo ammetto. Ma mi piacerebbe dipingerla di rosso per vedere se che mi fornisce nuove informazioni ", probabilmente Chesterton ne sarebbe contento.
Cort Ammon,

41
Il metodo di decodifica dell'orologio in modalità kernel del sistema operativo VMS SYS $ ASCTIM contiene una riga di codice che aiuta a correggere la deriva siderale dell'equinozio di primavera. Nessuno dei test unitari di DEC esercita questo codice. Funzionò una volta - correttamente - il 2000-02-28 alle 23: 59: 59: 999. Non funzionerà di nuovo fino all'anno 2400. Non eliminarlo.
AI Breveleri

13
@AIBreveleri Spero che ci sia un commento nel codice stesso che spieghi questo, e non semplicemente qui su StackExchange: D
Kyle Strand

11
@KyleStrand Di cosa stai parlando? Questa è la documentazione Riesci a pensare a un posto migliore per mettere informazioni cruciali in modo che altre persone le trovino rispetto a StackExchange? ;-)
Cort Ammon

43

Tendo anche al nome grepdella funzione / classe nel codice, che può dare alcuni vantaggi aggiuntivi che un analizzatore di codice potrebbe non avere, come ad esempio se il nome è menzionato in un commento o in un file di documentazione o in uno script. Corro grep sui file nell'albero dei sorgenti e memorizzo il risultato in un file; di solito il risultato fornisce informazioni condensate: nome / percorso del file, numero di riga e riga in cui viene incontrato il nome, che può fornire indizi su dove viene chiamata o menzionata la funzione / classe senza alcun significato semantico (contrariamente a un analizzatore di codice ) e indipendentemente dalle estensioni dei file. Sicuramente non la soluzione definitiva ma una bella aggiunta ad un'analisi.


11
Non sono un downvoter, ma cosa succede se la sezione di codice che sospetti non venga mai eseguita non è una funzione o classe completa ma una sezione di un metodo / funzione?
Tulains Córdova,

9
A seconda della lingua, ciò potrebbe non essere sempre affidabile: alcune lingue consentono di effettuare dinamicamente chiamate al metodo. Ad esempio php:$object->$variableMethod($arg1, $arg2);
Dezza

9
@ TulainsCórdova Quindi questa non è probabilmente una buona soluzione, ovviamente. Come per ogni potenziale soluzione, usala se ha senso in un determinato contesto. Ma forse l' greping. La funzione che comprende la sezione di codice può fornire indizi su come viene utilizzata la funzione e quindi sul suo contenuto?
piwi,

7
"come se il nome fosse menzionato in un commento o in un file di documentazione o in uno script" - O se qualcuno sta usando la reflection per chiamare il metodo (se si utilizza un linguaggio di alto livello / OO). Sebbene non sia ancora necessariamente accurato al 100%, poiché alcune lingue supportano alcuni modi molto ottusi di trovare e invocare metodi.
aroth

Ciò è utile se si ha accesso a tutto il codice possibile che potrebbe utilizzare la funzione. Tuttavia, se il codice fa parte di una libreria, come farai a sapere che qualcos'altro che non puoi cercare non si romperà?
Kevin Shea,

30
  • Identifica il codice che sembra morto (analisi statica, ecc.).
  • Aggiungi un messaggio di registro per ogni invocazione del presunto codice morto. È facile con funzioni / metodi; con membri statici come costanti è più complicato. A volte puoi semplicemente contrassegnare il codice come obsoleto e il runtime registra automaticamente un messaggio. Lascia il codice altrimenti intatto.
    • Registra un messaggio quando viene caricato il modulo morto: la maggior parte delle lingue ha un modo per eseguire l'inizializzazione statica al momento del caricamento, che può essere salvata in avanti.
    • Assicurati che il tuo messaggio di registro includa una ragionevole traccia dello stack, in modo da capire cosa ha chiamato il codice presumibilmente morto.
  • Esegui il codice modificato in tutte le tue suite di test. Le tue suite di test dovrebbero anche testare orari speciali, come attraversare una mezzanotte, un giro di un quarto, un giro di un anno, ecc. Guarda i registri, aggiorna la tua comprensione di ciò che è morto. Si noti che i test unitari possono testare specificamente il codice morto, mentre nessun altro test unitario e nessun test di integrazione lo toccano.
  • Esegui il codice modificato in produzione per alcune settimane. Assicurati che ogni processo periodico, come quei cron job ETL una volta al mese, vengano eseguiti durante questo periodo.
  • Guarda i registri. Tutto ciò che è stato registrato non è effettivamente morto. La chiusura transitiva del grafo delle chiamate sul resto del codice morto è anche potenzialmente non morti, anche se non è stato richiamato. Analizzalo. Forse alcune filiali sono morte in modo sicuro (ad esempio lavorando con un'API di un servizio ormai estinto), mentre altre non lo sono ancora. Forse un intero modulo / classe viene caricato solo per una costante definita in esso e puoi facilmente disattivarlo.
  • Tutto ciò che rimane è morto in modo sicuro e può essere rimosso.

C'è una prima volta per tutto, quindi solo perché il codice non è stato eseguito prima non significa che non verrà mai eseguito, in particolare se esiste un percorso di esecuzione. Detto questo, decidere di rimuovere qualcosa che può essere eseguito è un processo diverso dal determinare se potrebbe essere eseguito.

7
@nocomprende esattamente cosa succede se questo codice viene utilizzato solo per una funzione di fine anno ed esegui il test viene eseguito da febbraio a novembre. Non ne troverai l'utilizzo anche se lo hai profilato per quasi un anno intero.
Erik

1
@ 9000 Concordo in teoria, ma i sistemi legacy sono pieni di dipendenze nascoste che possono ostacolare questo tipo di test. Perché il tuo esempio di test funzioni, devi sapere che esiste un qualche tipo di accoppiamento temporale. Se l'accoppiamento temporale è esterno al codice che stai testando, non lo vedrai nel codice e sai che l'accoppiamento temporale è un fattore. Ad esempio il codice del silos A è installato nel GAC . Anni fa il silo B chiese al silo A di aggiungere un percorso di codice per un rapporto che doveva essere confrontato con i dati del silo A. cont.
Erik

1
@ 9000 cont. Ora il codice del silo B ha un accoppiamento temporale con un percorso del codice "morto" nel codice del silo A che non è evidente osservando semplicemente il codice del silo A. Come testereste l'unità per questo accoppiamento temporale poiché l'accoppiamento temporale è solo nel codice del silo B senza sapere di parlare con il silo B? Anche allora il tempo non è un fattore nel codice (silo A), quindi come sarebbe un test temporale?
Erik

2
Qualcuno ricorda il panico Y2k ? Qualche codice era corretto nel 2000 perché era sbagliato per il 2100! Abbiamo avuto un potenziale problema che potrebbe verificarsi una volta ogni 100 anni o anche una volta ogni 400 anni.
Steve Barnes,

16

Oltre alle risposte esistenti menzionate, è possibile anche rimuovere in modo iterativo il codice su più versioni.

Nella versione iniziale è possibile eseguire un avviso di deprecazione con il blocco di codice ancora funzionante. Nella versione successiva, è possibile rimuovere il blocco di codice ma lasciare un messaggio di errore che consente agli utenti di rendere obsoleta la funzionalità e non più disponibile. Nella versione finale rimuoveresti il ​​blocco di codice e tutti i messaggi.

Ciò può aiutare a identificare funzionalità impreviste senza preavviso per gli utenti finali. Nel migliore dei casi il codice non fa davvero nulla e tutto ciò che accade è che il codice non necessario viene conservato su più versioni prima di essere rimosso.


7
+1. Ho visto così tanti bug e problemi rivolti ai clienti che avrebbero potuto essere evitati se gli sviluppatori (o il loro management) fossero stati disposti a suddividere un progetto in due o tre fasi sicure invece di provare a buttare tutto in una volta per soddisfare un scadenza arbitraria prima di passare al prossimo progetto.
Ruakh

Questo risponde alla domanda posta nel titolo, ma se mi fai, la domanda ha il titolo sbagliato. Il titolo chiede come eliminare il codice, ma il corpo riguarda come identificare se il codice è sicuro da eliminare in primo luogo. Sicuramente rispondi alla prima, ma non sono sicuro che sia quello che l'OP stava chiedendo davvero.
underscore_d

7

Molte persone stanno suggerendo che la cosa "sicura" da fare è lasciare il codice se non riesci a dimostrare che non è in uso.

Ma il codice non è una risorsa, è una responsabilità.

A meno che tu non possa spiegare perché è importante e indicare i suoi test, l'alternativa "più sicura" potrebbe benissimo essere quella di eliminarlo.

Se non sei ancora sicuro, almeno assicurati di aggiungere test per esercitare il codice zombi.


Passaggio 1: elimina tutto il codice. Passaggio 2: rimettere ciò che è necessario. Passaggio 3: ripetere.

1
@Adrian Posso attirare la tua attenzione su Knight Capital? en.wikipedia.org/wiki/… - nel caso in cui tu voglia rafforzare il tuo punto con un riferimento ad esso. Fondamentalmente sono implosi perché alcuni vecchi codici sono tornati in vita e hanno gentilmente perso quasi $ 500 milioni per loro. La successiva indagine si concentrò sulle loro procedure di rilascio, ma il problema principale era "la funzionalità morta non era stata eliminata".
SusanW,

6

È possibile utilizzare gli interruttori funzione per modificare il percorso di esecuzione del software in modo da ignorare completamente il codice in questione.

In questo modo puoi distribuire in modo sicuro la modifica con il codice non in uso e disattivarlo. Se noti alcuni errori importanti relativi al codice, riattiva e attiva il possibile percorso.

Questo approccio dovrebbe darti sicurezza se non vedi problemi per un periodo di tempo prolungato, nonché la possibilità di riattivarlo in tempo reale senza dispiegamento. Tuttavia, un modo ancora migliore sarebbe quello di applicare anche la registrazione aggiuntiva e testare la copertura nell'area in questione che fornirà ulteriori prove se viene utilizzato o meno.

Maggiori informazioni sui toggle qui: https://martinfowler.com/articles/feature-toggles.html


6

Analisi statica ovviamente ... e la cosa bella è che non hai bisogno di nuovi strumenti; il tuo compilatore ha tutti gli strumenti di analisi statica di cui hai bisogno.

Basta cambiare il nome del metodo (ad es. Passare DoSomethinga DoSomethingX) e quindi eseguire una build.

Se la tua build ha esito positivo, il metodo, ovviamente, non viene utilizzato da nulla. Sicuro da cancellare.

Se la compilazione fallisce, isola la riga di codice che la chiama e controlla per vedere quali ifistruzioni circondano la chiamata per determinare come attivarla. Se non riesci a trovare alcun possibile caso di utilizzo dei dati che lo attivi, puoi eliminarlo in sicurezza.

Se sei davvero preoccupato di eliminarlo, considera di conservare il codice ma di contrassegnarlo con un ObsoleteAttribute (o equivalente nella tua lingua). Rilascialo così per una versione, quindi rimuovi il codice dopo che non sono stati rilevati problemi in natura.


8
Questo è un buon consiglio per i linguaggi di programmazione che non hanno un'associazione dinamica / tardiva completa . Tuttavia, per quelli che hanno, è più complicato. E, naturalmente, il codice potrebbe utilizzare il metodo anche se non è mai stato utilizzato in produzione.
eis

La domanda presuppone la risoluzione dei simboli in fase di compilazione ( Quindi hai trovato del codice che sembra superfluo. Il compilatore non se ne accorge. ). E anche la funzione o gli oggetti associati in ritardo sono in genere definiti in due punti, ad esempio un'interfaccia o un file di mappa, quindi la generazione non riuscirà comunque.
John Wu,

1
Hmm ... Sono incuriosito, non credo che quest'ultimo che dici sia vero. Non è per esempio vaniglia javascript (non precompilato), ruby, python o smalltalk, giusto? Se ti riferisci a cose che non esistono, tali errori si verificano solo in fase di esecuzione.
eis

Forse non siamo d'accordo su cosa si intenda per "legame" che per me indica la risoluzione di una rappresentazione logica a una rappresentazione fisica, ad esempio un simbolo a un indirizzo. Le funzioni Javascript sono memorizzate come coppie tag / valore all'interno dell'oggetto contenitore, quindi qualsiasi nozione di associazione sembra non sequitur.
John Wu,

1
Sì, non siamo davvero d'accordo. Il termine ha un significato diverso su lingue che non hanno un'associazione tardiva completa (come ho spiegato nel primo commento). Quando si associano i metodi in fase di esecuzione - in lingue che possono aggiungere e rimuovere metodi in fase di esecuzione, al volo - si sta utilizzando l'associazione tardiva nel modo in cui ho capito che deve essere fatto. Questo è vero nei linguaggi in stile ruby ​​/ python / smalltalk, e lì non hai file di mappa separati. Dato che in particolare il rubino e il pitone sono molto usati, non sono d'accordo sulla tua idea di cosa significhi "tipicamente".
eis,

4
  • Vorrei iniziare a utilizzare un analizzatore di codice per verificare se si tratta di un codice morto
  • Vorrei iniziare a controllare i miei test e provare a raggiungere il codice. Se non riesco a raggiungere il codice all'interno di un caso di test, potrebbe trattarsi di un codice morto
  • Vorrei rimuovere il codice e generare invece un'eccezione. Per una versione l'eccezione è attivata e nella seconda versione è possibile rimuovere l'eccezione. Per sicurezza, posiziona un flag di emergenza (in Java, una proprietà di sistema) per poter attivare il codice originale, quando un cliente nota l'eccezione. Quindi l'eccezione può essere disabilitata e il codice originale può essere attivato in un ambiente di produzione.

6
-1. Non è assolutamente consigliabile apportare queste modifiche al codice produttivo, al flag di sicurezza o meno. Solo quando si è sicuri che il codice in questione non sia utilizzato, dovrebbe essere rimosso nel codice produttivo. Questo tipo di pasticciare è esattamente il motivo per cui dovrebbero sempre esserci diversi ambienti produttivi e di prova.
Johannes Hahn,

3
Funzionerà in modo fantastico quando la copertura del test è quasi del 90%, ma cosa succede se no e hai un progetto con 100.000 righe di codice. Ecco perché ho descritto per provare a testare il codice e se nessun test è in grado di raggiungere il codice ... potrebbe essere morto.
Markus Lausberg

IMHO Se non sei abbastanza sicuro di trattenerti dalla rimozione del codice - non dovresti generare eccezioni nella produzione - la registrazione suggerita dall'OP è un'alternativa molto migliore, raggiunge lo stesso e puoi ottenere la diagnostica invece di fare affidamento sui reclami dei clienti
Sebi

@JohannesHahn Penso che intendi "codice di produzione", che è il codice che viene eseguito in un ambiente di produzione.
jpmc26

@ jpmc26 Sì, certo.
Johannes Hahn,

3

Rimozione di codice non raggiungibile

In un linguaggio di principio staticamente tipizzato, dovresti sempre sapere se il codice è effettivamente raggiungibile o meno: rimuovilo, compila, se non c'è errore non è raggiungibile.

Sfortunatamente, non tutte le lingue sono tipicamente statiche e non tutte le lingue tipicamente statiche sono basate su principi. Le cose che potrebbero andare male includono (1) la riflessione e (2) un sovraccarico senza principi.

Se si utilizza un linguaggio dinamico o un linguaggio con una riflessione sufficientemente potente da consentire l'accesso al pezzo di codice esaminato in fase di esecuzione tramite reflection, non è possibile fare affidamento sul compilatore. Tali lingue includono Python, Ruby o Java.

Se si utilizza una lingua con sovraccarico senza principi, la semplice rimozione di un sovraccarico potrebbe semplicemente commutare la risoluzione del sovraccarico su un altro sovraccarico in silenzio. Alcune di queste lingue consentono di programmare un avviso / errore in fase di compilazione associato all'utilizzo del codice, altrimenti non è possibile fare affidamento sul compilatore. Tali linguaggi includono Java (uso @Deprecated) o C ++ (uso [[deprecated]]o = delete).

Quindi, a meno che tu non sia molto fortunato a lavorare con linguaggi rigorosi (mi viene in mente Rust), potresti davvero spararti al piede fidandoti del compilatore. E sfortunatamente le suite di test sono generalmente incomplete, quindi non c'è molto altro aiuto.

Indica la prossima sezione ...


Rimozione di codice potenzialmente inutilizzato

Più probabilmente, al codice viene effettivamente fatto riferimento, tuttavia si sospetta che in pratica i rami del codice che fanno riferimento non vengano mai presi.

In questo caso, indipendentemente dalla lingua, il codice è raggiungibile in modo dimostrabile e può essere utilizzata solo la strumentazione di runtime.

In passato, ho usato con successo un approccio in 3 fasi per rimuovere tale codice:

  1. Su ogni ramo sospettato di NON essere preso, registra un avviso.
  2. Dopo un ciclo, genera un'eccezione / restituisci un errore quando inserisci il codice specifico.
  3. Dopo un altro ciclo, eliminare il codice.

Che cos'è un ciclo? È il ciclo di utilizzo del codice. Ad esempio, per un'applicazione finanziaria mi aspetterei un breve ciclo mensile (con gli stipendi pagati alla fine del mese) e un lungo ciclo annuale. In questo caso, è necessario attendere almeno un anno per verificare che non venga mai emesso alcun avviso per l'inventario di fine anno che potrebbe utilizzare percorsi di codice che altrimenti non sarebbero mai stati utilizzati.

Si spera che la maggior parte delle applicazioni abbia cicli più brevi.

Consiglio di inserire un commento su TODO, con una data, indicando quando passare al passaggio successivo. E un promemoria nel tuo calendario.


1
In realtà, in C ++ puoi contrassegnare il tuo sovraccarico sospetto [[deprecated]]per identificare i siti che chiamano quella versione; quindi puoi ispezionarli per vedere se il loro comportamento cambierà. In alternativa, definisci il tuo sovraccarico come = delete- in questo caso, dovrai usare esplicitamente argomenti per usare una delle altre versioni.
Toby Speight,

@TobySpeight: è vero, e anche una buona alternativa. Vorrei modificarlo in.
Matthieu M.

"Dottore, fa male quando lo faccio." "Beh , non farlo! " Non usare approcci che rendono il codice ingestibile.

2

Rimuovere il codice dalla produzione è come pulire la casa. Nel momento in cui butti via un oggetto dalla soffitta, tua moglie ti ucciderà il giorno successivo per aver gettato via il dono di ritorno a casa del terzo pronipote della sua bisnonna dal loro vicino che morì nel 1923.

Scherzi a parte, dopo un'analisi superficiale utilizzando vari strumenti che tutti hanno già menzionato e dopo aver utilizzato l'approccio di deprecazione a gradini già menzionato, tieni presente che potresti essere uscito dalla società quando si verifica l'effettiva rimozione. Lasciare che il codice rimanga e che la sua esecuzione sia registrata e che sia garantito che tutti gli avvisi della sua esecuzione siano comunicati a te (o ai tuoi successori e cessionari) è essenziale.

Se non segui questo approccio, probabilmente verrai ucciso da tua moglie. Se mantieni il codice (come ogni ricordo), puoi riposare la tua coscienza nel fatto che la legge di Moore viene in tuo soccorso e il costo dello spazio su disco spazzatura usato dal codice che sembra una "roccia nella mia scarpa", riduce ogni anno e non rischi di diventare il centro di più pettegolezzi di raffreddamento dell'acqua e sguardi strani nei corridoi.

PS: Per chiarire in risposta a un commento .. La domanda originale è "Come si elimina in modo sicuro ..", naturalmente, nella mia risposta si assume il controllo della versione. Proprio come scavare nella spazzatura per trovare il prezioso è assunto. Nessun ente con alcun senso butta via il controllo del codice e della versione dovrebbe far parte del rigore di ogni sviluppatore.

Il problema riguarda il codice che potrebbe essere superfluo. Non possiamo mai sapere che un pezzo di codice è superfluo a meno che non possiamo garantire che il 100% dei percorsi di esecuzione non lo raggiungano. E si presume che si tratti di un software abbastanza grande da rendere impossibile questa garanzia. E a meno che non abbia letto la domanda in modo errato, l'unica volta che questo intero thread di conversazione è rilevante è per i casi durante la produzione in cui il codice rimosso potrebbe essere chiamato e quindi abbiamo un problema di runtime / produzione. Il controllo della versione non salva nessuno dietro a causa di errori di produzione, quindi il commento su "Controllo della versione" non è pertinente alla domanda o al mio punto originale, vale a dire, se necessario, deprecare correttamente in un arco di tempo estremamente lungo, altrimenti non '

IMHO, il commento è superfluo ed è un candidato per la rimozione.


12
Se avessi l'equivalente del controllo della versione , l'obiezione di mia moglie sarà facilmente annullata. Anche con l'eliminazione del codice: non è un'offesa mortale. Il repository non viene mai gettato via.
JDługosz,

Ho pensato che la programmazione orientata agli oggetti avrebbe dovuto restringere l'ambito in cui un metodo poteva essere chiamato. Quindi dovrebbe portare a un codice che è più facilmente comprensibile e gestibile, giusto? Altrimenti, perché lo stiamo facendo in questo modo?

@nocomprende In nessuna parte di questo thread è indicato che la discussione è limitata al design / alle lingue OOP. Non sono sicuro del motivo per cui ritieni che sia rilevante.
underscore_d

1

Forse il compilatore lo nota, semplicemente non fa storie.

Esegui una build con ottimizzazioni complete del compilatore, orientate alla dimensione. Quindi eliminare il codice sospetto ed eseguire nuovamente la build.

Confronta i binari. Se sono identici, il compilatore ha notato e eliminato in silenzio il codice. Puoi eliminarlo dalla fonte in modo sicuro.

Se i binari sono diversi ... allora non è conclusivo. Potrebbe essere qualche altro cambiamento. Alcuni compilatori includono anche la data e l'ora della compilazione nel file binario (e forse può essere configurato via!)


1
Non tutte le lingue sono compilate, non tutti i compilatori hanno ottimizzazione e non tutte le ottimizzazioni sono uguali, molti non rimuovono il codice non invocato solo nel caso in cui il codice sia presente per un motivo. Se il codice risiede in una libreria / DLL condivisa, non verrà eliminato.
Steve Barnes,

1
@SteveBarnes Beh, sì, se non stai facendo LTO e collegando staticamente tutto, hai un po 'di dolore. Questo è un dato.
Navin,

1

Recentemente mi sono imbattuto in questa situazione esatta, con un metodo chiamato "deleteFoo". In nessuna parte dell'intera base di codice c'era quella stringa trovata diversa dalla dichiarazione del metodo, ma saggiamente ho scritto una riga di registro nella parte superiore del metodo.

PHP:

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

Si scopre che il codice è stato usato! Alcuni metodi AJAX chiamano "method: delete, elem = Foo" che viene poi concatenato e chiamato con call_user_func_array () .

In caso di dubbio, accedi! Se hai trascorso abbastanza tempo senza vedere il registro riempito, allora considera di rimuovere il codice. Ma anche se rimuovi il codice, lascia il registro in posizione e un commento con la data per rendere più facile trovare il commit in Git per il ragazzo che deve mantenerlo!


Ci sono librerie per PHP e Perl chiamate Tombstone (php: github.com/scheb/tombstone ) che possono essere utili qui.
Alister Bulman,

0

Utilizzano grepo qualsiasi strumento che avete a disposizione prima, si potrebbe trovare riferimenti al codice. (Non originariamente le mie istruzioni / consigli.)

Quindi decidere se si desidera rischiare di rimuovere il codice o se il mio suggerimento potrebbe venire a utilizzare:

È possibile modificare la funzione / blocco di codice per scrivere che è stato utilizzato in un file di registro. Se tale messaggio non viene "mai" registrato nel file di registro, è possibile rimuovere probabilmente il codice di registrazione.

Due problemi:

  1. Ottenere il registro da altri utenti. Forse puoi impostare un flag globale che arresta in modo anomalo il programma all'uscita e chiede gentilmente all'utente di inviarti il ​​registro degli errori.

  2. Traccia del chiamante. In alcune lingue di alto livello (es. Python) puoi facilmente ottenere un traceback. Ma nei linguaggi compilati dovrai salvare l'indirizzo di ritorno nel registro e forzare un dump principale all'uscita (punto 1).
    In C, questo dovrebbe essere abbastanza semplice: usa un blocco condizionale (es. #Ifdef ... #endif) per usarlo solo quando sai che funzionerà (e hai provato che lo fa), e leggi semplicemente l'indirizzo di ritorno dallo stack (può o meno richiedere un linguaggio assembly inline).

Il salvataggio degli argomenti nel file di registro può o non può essere utile.


0

Se sai cosa fa il codice che lo circonda, testalo per vedere se si rompe. Questo è il modo più semplice ed economico per refactificare un pezzo di codice su cui stai lavorando e dovrebbe essere fatto comunque per sviluppare qualsiasi modifica al codice su cui stai lavorando in primo luogo.

Se, d'altra parte, stai refactoring un pezzo di codice in cui non sai cosa fa, non rimuoverlo . Comprenderlo prima, quindi riformattalo se lo ritieni sicuro (e solo se riesci a determinare che il refactoring sarebbe un uso corretto del tuo tempo).

Ora, potrebbero esserci alcune circostanze (so di essermi imbattuto da solo) in cui il codice "morto" non è in realtà collegato a nulla . Come le librerie di codice sviluppate da un appaltatore che non sono mai state implementate da nessuna parte perché sono diventate obsolete immediatamente dopo la consegna dell'app (perché sì, ho in mente un esempio specifico, come lo sapevi?).

Personalmente ho finito per rimuovere tutto questo codice, ma è un rischio enorme che non raccomando di prendere alla leggera - a seconda di quanto codice potrebbe potenzialmente avere questo cambiamento (perché c'è sempre il potenziale che hai trascurato qualcosa) tu dovrebbe essere straordinariamente attento ed eseguire test unitari aggressivi per determinare se la rimozione di questo antico codice legacy interromperà l'applicazione.

Ora, detto tutto ciò, probabilmente non vale la pena dedicare tempo e sanità mentale a provare a rimuovere il codice in questo modo. A meno che non stia diventando un problema serio con la tua applicazione (ad esempio, non essere in grado di completare le scansioni di sicurezza a causa del gonfiamento dell'applicazione ... perché sì, ho ancora un esempio in mente), quindi non lo consiglierei a meno che tu non raggiunga una situazione del genere in cui il codice ha davvero iniziato a marcire in modo efficace.

Quindi in breve: se sai cosa fa il codice, testalo prima. In caso contrario, probabilmente puoi lasciarlo da solo. Se sai di non poterlo lasciare da solo, dedica il tuo tempo a testare la modifica in modo aggressivo prima di implementare la modifica.


0

Il mio approccio, che in questo caso ho sempre considerato lo standard del settore, stranamente non è stato menzionato finora:

Ottieni un secondo paio di occhi.

Esistono diversi tipi di "codice inutilizzato" e in alcuni casi l'eliminazione è appropriata, in altri casi è necessario riformattarlo e in altri casi si scappa il più lontano possibile e non si guarda mai indietro. Devi capire quale è, e per farlo devi accoppiarti meglio con un secondo esperto! - sviluppatore, uno che conosce molto bene la base di codice. Ciò riduce significativamente il rischio di errori non necessari e consente di apportare i miglioramenti necessari per mantenere la base di codice in uno stato mantenibile. E se non lo fai, a un certo punto rimani senza sviluppatori che hanno molta familiarità con la base di codice.

Ho visto anche l'approccio di registrazione - per essere più precisi, ho visto il codice di registrazione: ancora più codice morto che è stato lasciato lì per 10 anni. L'approccio di registrazione è abbastanza decente se stai parlando di rimuovere intere funzionalità, ma non se stai solo rimuovendo un piccolo pezzo di codice morto.


0

La semplice risposta alla tua domanda è che non puoi eliminare in modo sicuro il codice che non capisci, il che include non capire come viene chiamato. Ci saranno dei rischi.

Dovrai giudicare il modo migliore per mitigare quel rischio inevitabile. Altri hanno menzionato la registrazione. Naturalmente la registrazione può dimostrare che viene utilizzata, ma non può dimostrare che non lo sia. Poiché il problema principale con il codice morto è che viene mantenuto, potresti essere in grado di cavartela aggiungendo un commento dicendo che si tratta di un codice morto sospetto e di non fare alcuna manutenzione su di esso.

Se il codice è sotto il controllo del codice sorgente, puoi sempre scoprire quando è stato aggiunto e determinare come è stato chiamato.

Alla fine, o lo capisci, lo lasci da solo o rischi.


0

Nel caso in cui lo sviluppatore del codice in questione sia ancora disponibile, rivedi con lui la rimozione del codice . Inoltre, leggi i commenti nel codice .

Otterrai la spiegazione corretta, perché questo codice è stato aggiunto e per quale funzione serve. Può essere facilmente supporto per il caso d'uso specifico, oppure potresti non avere abbastanza esperienza per giudicare se non è realmente necessario. In casi estremi, è possibile rimuovere tutte le istruzioni gratuite da un programma C e non notare nulla di sbagliato (potrebbero essere necessari alcuni minuti per esaurire la memoria).

È davvero una cattiva cultura quando l'autore del codice si siede accanto a te, eppure non vedi alcun motivo per parlare perché sei sicuro che scriva solo codice cattivo e il tuo è tutto così perfetto. E, naturalmente, scrive solo parole casuali nei commenti, non c'è bisogno di perdere tempo a leggere.

Uno dei motivi più importanti per scrivere un commento nel codice è spiegare PERCHÉ è stato implementato in questo modo. Potresti non essere d'accordo con la soluzione, ma devi prendere in considerazione il problema.

La discussione con l'autore può anche rivelare che il codice è il residuo storico di qualcosa rimosso o mai finito, quindi è sicuro da eliminare.


-1

Concordo pienamente con il primo punto di Markus Lausberg: il codice dovrebbe assolutamente essere analizzato con l'aiuto di strumenti. I cervelli delle scimmie non sono affidabili con questo tipo di attività !!

La maggior parte dei linguaggi di programmazione standard può essere utilizzata negli IDE e ogni IDE che merita di essere chiamato con una funzione "trova per tutti gli usi" che è esattamente lo strumento giusto per questo tipo di situazione. (Ctrl + Maiusc + G in Eclipse per esempio)

Naturalmente: gli strumenti dell'analizzatore statico non sono perfetti. Bisogna essere consapevoli di situazioni più complicate in cui la decisione di chiamare il metodo in questione avviene solo in fase di esecuzione e non appena (chiamate tramite Reflection, chiamate a funzioni di "valutazione" nei linguaggi di scripting ecc.).


5
Forse i cervelli delle scimmie non dovrebbero scrivere codice? "Non toccato da mani umane " era una frase di marketing. Ho sempre immaginato le mani di gorilla che facevano il lavoro.

3
(Anche il cervello delle scimmie ha scritto gli strumenti) (Shhh!

1
Penso che manchi entrambi il mio punto. Ho detto chiaramente che non esiste alcuna garanzia che l'analisi del codice statico troverà ogni invocazione di un metodo o di una classe. È, come si dice sulla scatola, un'analisi statica. Non è possibile e non ci si dovrebbe aspettare di trovare invocazioni dinamiche tramite reflection, eval () ecc. Il mio punto è che l'analisi del codice statico dovrebbe essere considerata un prerequisito necessario per eliminare un pezzo di codice. E più grande è il progetto, meno è probabile che un cervello umano sia persino in grado di fare quell'analisi e tanto meno di farlo correttamente ...
Johannes Hahn

1
... l'analisi del codice statico è un lavoro noioso e ripetitivo, che è meglio fare da un computer. Esattamente il tipo di attività che può essere eseguita in modo affidabile dai computer ma è notoriamente inaffidabile quando eseguita da un cervello di scimmia. Quest'ultimo dovrebbe essere usato solo per cose che un computer non può fare come trovare quelle riflessioni difficili e le chiamate di valutazione.
Johannes Hahn,

1
Ancora una volta, manca il punto. Anche se gli strumenti non sono perfetti perché sono stati scritti da cervelli di scimmie (e non sono nemmeno sicuro di doverlo concedere! Il calcolo delle gerarchie di chiamate statiche non è così difficile), sono comunque di gran lunga superiori all'esecuzione manuale delle attività. Questa non è una situazione del tutto o niente. Uno strumento affidabile al 99% è ancora preferibile a un cervello che forse inizia con il 99% per cento ma scende rapidamente a tassi molto più bassi dopo le prime mille righe di codice.
Johannes Hahn,

-1

Due possibilità:

  1. Hai una copertura di prova del 99% + : elimina il codice e completalo.
  2. Non disponi di una copertura test attendibile: la risposta è aggiungere un avviso di deprecazione . Vale a dire, aggiungi un po 'di codice in quel posto che fa qualcosa di sano (registra un avviso in un posto facilmente visibile che non è in conflitto con l'uso quotidiano della tua applicazione, per esempio). Quindi lasciarlo sobbollire per alcuni mesi / anni. Se non ne hai mai trovato alcuna occorrenza, sostituisci la deprecazione con qualcosa che genera un'eccezione. Lascialo riposare per un po '. Infine, rimuovi tutto completamente.

2
ciò non sembra offrire nulla di sostanziale rispetto ai punti formulati e spiegati nelle precedenti 17 risposte. Sia la copertura che l'avvertimento di deprecazione furono discussi già là
moscerino il

@gnat, hai ragione. Quando ho scansionato le risposte per la prima volta, per qualche motivo ho pensato di averne viste diverse che in cima non sono arrivate in breve tempo a questi due punti. Nel mezzo delle risposte ce ne sono alcune così.
AnoE
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.