Il codice più vecchio dovrebbe essere aggiornato per utilizzare costrutti di linguaggio più recenti, o con costrutti obsoleti che potrebbero rimanere bloccati?

15

Voglio apportare alcuni miglioramenti in qualche codice ancora funzionante che è stato scritto molto tempo fa, prima che il linguaggio di programmazione in cui è scritto cresca nelle funzionalità. In teoria, l'intero progetto utilizza la versione aggiornata della lingua; tuttavia, questo particolare modulo (e in effetti, molti altri moduli) sono ancora scritti nel vecchio dialetto.

Dovrei:

  • Non toccare le parti del codice che non devo toccare, ma scrivi la mia patch facendo uso delle nuove funzionalità del linguaggio che rendono più facile scrivere la patch ma non vengono utilizzate in situazioni analoghe in nessun'altra parte del modulo? (Questa è la soluzione che avrei scelto intuitivamente.)
  • Ignorare il fatto che siano passati anni e riflettere lo stile che viene utilizzato nel resto del codice durante la scrittura della patch, comportandosi in modo efficace come se stessi facendo lo stesso compito molti anni prima? (Questa soluzione mi sembra intuitiva, ma data la quantità di confusione, chiunque parli di "buon codice" fa in modo di mantenere la coerenza a tutti i costi, forse è questo che dovrei fare.)
  • Aggiorna l'intero modulo per utilizzare i costrutti e le convenzioni del linguaggio più recenti? (Questa è probabilmente la soluzione migliore, ma potrebbe richiedere un sacco di tempo ed energia che potrebbero essere meglio spesi per un compito diverso.)
posta gaazkam 25.03.2017 - 00:17
fonte

5 risposte

10

È impossibile dare una risposta definitiva a questa domanda, perché dipende troppo dai particolari della situazione.

La coerenza nello stile del codebase è importante perché aiuta a rendere il codice più facile da capire, che è uno degli aspetti più importanti della manutenibilità.
Non saresti il primo programmatore a essere maledetto all'inferno per aver reso il codice difficile da capire mescolando diversi stili. Potrebbe anche essere te stesso che maledice tra un anno.

D'altra parte, l'uso di costrutti di linguaggio nuovi che non esistevano quando il codice è stato scritto per la prima volta non implica automaticamente che si stia riducendo la manutenibilità o addirittura che si stia rompendo lo stile del codice. Tutto dipende dalle caratteristiche linguistiche particolari che si desidera utilizzare e dalla familiarità con il team sia con il vecchio stile del codice sia con le nuove funzionalità.

Ad esempio, in generale non sarebbe consigliabile iniziare a introdurre concetti di programmazione funzionale come map / reduce a un codebase che è completamente in stile OO, ma potrebbe funzionare per aggiungere una funzione lambda qua e là a un programma che non ha non usare mai nulla del genere prima.

    
risposta data 25.03.2017 - 10:38
fonte
5

In larga misura, il codice e il suo aspetto sono irrilevanti . Ciò che conta è il codice .

Se puoi garantire che cambiare / riscrivere il codice non cambierà ciò che fa il codice, allora puoi procedere e rifattorizzare il codice a tuo piacimento. Quella "garanzia" è una serie completa di test unitari che puoi eseguire prima e dopo i tuoi cambiamenti, senza differenze percettibili.

Se non puoi garantire quella stabilità (non hai quei test), allora lascia stare tutto da solo.

Nessuno ti ringrazierà per aver "rotto" un software business-critical, anche se cercavi di renderlo "migliore". "Lavorare" briscola "meglio" ogni volta.

Naturalmente, non c'è niente che ti impedisca di creare un set di tali test in prontezza per un simile esercizio ...

    
risposta data 28.03.2017 - 12:30
fonte
3

Quasi tutto può essere analizzato in termini di costi e benefici, e penso che questo si applichi qui.

Gli ovvi vantaggi della prima opzione sono che minimizza il lavoro a breve termine e riduce al minimo le possibilità di rompere qualcosa riscrivendo il codice di lavoro. Il costo ovvio è che introduce incoerenza nella base di codice. Quando esegui un'operazione X, viene eseguita in un modo in alcune parti del codice e in un modo diverso in una parte diversa del codice.

Per il secondo approccio, hai già notato l'ovvio beneficio: coerenza. Il costo ovvio è che devi piegare la mente per lavorare in modi che potresti aver abbandonato in passato anni fa e il codice rimane costantemente illeggibile.

Per il terzo approccio, il costo ovvio è semplicemente dover fare molto più lavoro. Un costo meno ovvio è che potresti rompere le cose che stavano funzionando. Questo è particolarmente probabile se (come spesso accade) il vecchio codice ha test inadeguati per assicurare che continui a funzionare correttamente. L'ovvio vantaggio è che (supponendo che lo fai con successo) hai un codice nuovo e brillante. Oltre all'utilizzo di nuovi costrutti del linguaggio, hai la possibilità di ridefinire il codice base, che quasi sempre darà dei miglioramenti in sé, anche se hai ancora usato il linguaggio esattamente come esisteva quando è stato scritto - aggiungi nuovi costrutti che rendono il linguaggio lavoro più facile, e potrebbe essere una vittoria importante.

Un altro punto importante: al momento, sembra che questo modulo abbia avuto una manutenzione minima per un lungo periodo (anche se il progetto nel suo complesso viene mantenuto). Ciò tende ad indicare che è abbastanza ben scritto e relativamente privo di bug - altrimenti, probabilmente avrebbe subito più manutenzione nel frattempo.

Questo porta a un'altra domanda: qual è la fonte del cambiamento che stai facendo ora? Se stai correggendo un piccolo bug in un modulo che nel complesso soddisfa ancora bene i suoi requisiti, questo tenderebbe ad indicare che il tempo e gli sforzi per il refactoring dell'intero modulo rischiano di essere in gran parte sprecati - nel momento in cui qualcuno deve pasticciare di nuovo, potrebbero essere più o meno nella stessa posizione in cui ti trovi ora, mantenendo il codice che non soddisfa le aspettative "moderne".

È anche possibile, tuttavia, che i requisiti siano cambiati e che tu stia lavorando al codice per soddisfare questi nuovi requisiti. In questo caso, è probabile che i tuoi primi tentativi non soddisfino effettivamente i requisiti attuali. C'è anche una possibilità sostanzialmente maggiore che i requisiti verranno sottoposti ad alcuni cicli di revisione prima che si stabilizzino di nuovo. Ciò significa che è molto più probabile che tu stia facendo un lavoro significativo in questo modulo nel (relativamente) breve termine, e molto più probabile che apporti cambiamenti nel resto del modulo, non solo in quella area che conosci di giusto adesso. In questo caso, è molto più probabile che il refactoring dell'intero modulo abbia vantaggi tangibili a breve termine che giustificano il lavoro extra.

Se i requisiti sono cambiati, potrebbe anche essere necessario considerare quale tipo di cambiamento è coinvolto e cosa sta guidando tale cambiamento. Ad esempio, supponiamo che tu stia modificando Git per sostituire il suo uso di SHA-1 con SHA-256. Questo è un cambiamento nei requisiti, ma l'ambito è chiaramente definito e piuttosto ristretto. Dopo aver effettuato l'archiviazione e l'uso corretto di SHA-256, è improbabile che si verifichino altre modifiche che riguardano il resto della base di codice.

Nella direzione opposta, anche quando inizia piccolo e discreto, una modifica all'interfaccia utente ha la tendenza a mongolfiera, quindi ciò che è iniziato come "aggiungi una nuova casella di controllo a questo schermo" si conclude più come: "modifica questa UI fissa supporta modelli definiti dall'utente, campi personalizzati, schemi di colori personalizzati, ecc. "

Per il primo esempio, probabilmente ha più senso minimizzare le modifiche ed errare dal lato della consistenza. Per quest'ultimo, è molto più probabile che il refactoring completo sia più redditizio.

    
risposta data 27.03.2017 - 20:44
fonte
1

Vorrei andare per te prima opzione. Quando eseguo il codice, seguo la regola per lasciarlo in uno stato migliore di quello che era.

Quindi il nuovo codice segue le best practice, ma non toccherò codice non correlato ai problemi su cui sto lavorando. Se lo fai bene, il tuo nuovo codice dovrebbe essere più leggibile e manutenibile quindi il vecchio codice. Ho scoperto che la manutenibilità totale migliora, perché se continui così, il codice che devi toccare per il tuo lavoro è sempre più spesso il codice "migliore". Il che porta a una risoluzione più rapida dei problemi.

    
risposta data 28.03.2017 - 12:43
fonte
1

La risposta, come tante altre cose, è dipende . Se ci sono grandi vantaggi nell'aggiornare i costrutti precedenti, come la manutenzione a lungo termine notevolmente migliorata (evitando l'inferno di callback, per esempio) piuttosto che farlo. Se non c'è alcun vantaggio importante, la coerenza è probabilmente il tuo amico

Inoltre, eviterai anche di incorporare due stili nella stessa funzione più di quanto desideri evitarlo in due funzioni separate.

Per riassumere: la tua decisione finale dovrebbe essere basata su un'analisi di 'costo' / 'beneficio' del tuo caso particolare.

    
risposta data 28.03.2017 - 12:52
fonte

Leggi altre domande sui tag