2010-01-20 18 views
36

Una delle situazioni più spiacevoli (e sfortunatamente più frequenti) nella mia vita di tutti i giorni come sviluppatore è quella di dover correggere bug o aggiungere funzionalità in codice mal progettato. Ora come un buon artigiano vorrei lasciare il codice in uno stato migliore di quello che ho trovato. Spesso le nuove funzionalità non possono essere implementate se non rifatto il progetto. Bene - potevano, ma questo avrebbe reso il codice ancora peggiore.Come gestire il codice errato

Sfortunatamente questo è esattamente quello con cui tendo ad avere difficoltà. Sento che se c'è una cosa che è difficile, è il refactoring del codice errato, specialmente quando si hanno scadenze. Toccando codice cattivo e complesso che lavora più o meno fa paura. Come risultato, introduco ancora più confusione quando inserisco una nuova funzionalità nel codice senza modificare il codice esistente.

Ora la mia domanda è Come posso imparare a gestire il codice errato? Come posso imparare a capire enormi basi di codice e poi a rifattorici alcune parti senza rompere cose che già funzionavano e senza superare la scadenza? C'è qualche letteratura che puoi raccomandare? Hai qualche consiglio generale per me?

+0

wiki della comunità? – Thilo

+0

Sì, wiki della comunità! – bitbonk

risposta

19

Michael Feathers ha scritto un buon libro su questo argomento esattamente.

Working Effectively with Legacy Code.

Un altro grande libro di Martin Fowler, Kent Beck e altri:

Refactoring: Improving the Design of Existing Code.

+0

+1 per WELC - è il classico riferimento - per una buona ragione! - esattamente per queste domande. –

+2

I libri sono una coppia. Consiglio di leggere prima * Refactoring *. Ispirerà e frustrerà, perché dirai: "Ma ho bisogno di apportare modifiche per renderlo testabile. Come faccio a farlo senza infrangere il codice?" È qui che entra in gioco il libro di Feathers. –

21

punta Generale:

if (it is working) 
    Do (not touch it); 
else 
{ 
    Make (as few modifications as possible) 
    Or (it will stop working at all); 
} 

Questa è l'esperienza di generazioni.

+15

+1. Triste ma vero. – Thilo

+7

Sono d'accordo, ma non ti frustra vedere come è fatto male? È come DEVO FISSARE QUESTO! – Strawberry

+2

Ogni programmatore ha questo;) ma il tempo, e quindi il denaro, non lo permetterà. – Oxymoron

6

quando ho a che fare con l'aggiunta di funzionalità per il codice cattivo, il mio approccio al solito è:

test
  • Scrivi automatizzati per ogni caratteristica importante che deve lavorare (come la maggior parte del codice male non ha alcun test).
  • Cambia codice.
  • Assicurarsi che i test siano ancora funzionanti.

Questo ti dà almeno una certa sicurezza che non hai infranto tutto. Per quanto riguarda come imparare a gestire il brutto codice, immagino che si tratti solo dell'esperienza.

+7

Molto spesso il codice è progettato così male che è difficile scrivere test per questo. – bitbonk

+0

È sempre possibile scrivere almeno * alcuni * test. –

+2

È possibile scrivere almeno test contro le interfacce di massimo livello. Se non ci sono buone interfacce, aggiungi un wrapper che * dia * una buona interfaccia, quindi scrivi i test a tale scopo. – Ether

4

Beh, se avete intenzione di refactoring grandi quantità di codice in un progetto mi consiglia di utilizzare un po 'decente controllo di versione, in modo da poter diramare e ripiegare facilmente. Dato, questa è probabilmente una porta aperta, ma fondamentale.

Inoltre, prima di iniziare a entrare in un OO complesso, provare a suddividere metodi e funzioni in più piccoli. Garantire un certo livello di atomicità in ciascuna delle funzioni, il che rende il codice molto più semplice da mantenere, leggere e gestire. Si tratta di piccole cose, scomposizione in unità logiche di operazione, sto facendo un'azione di refactoring su un metodo di 1k lines. Fa un sacco di cose fantasiose. Il mio primo obiettivo è quello di ricavarne tante cose in parti più piccole, quando ciò accadrà comincerò a pensare a un design OO migliore, che è molto più facile perché ho una comprensione molto migliore delle cose.

Anche l'aspirina funziona bene.

8

Il refactoring richiede l'imbracatura di sicurezza di un'unità di test dell'unità per rimuovere "L'ho rotto?" sensazione. Coprire il codice errato in una coperta di test ti aiuterà mentre cerchi di ottenere un buon codice pulito.

Pex è uno strumento che ritengo utile (se si è nel mondo .NET) per la creazione di test per il codice legacy.

Codice precedente == codice senza test!

Gentilezza,

Dan

0

Dipende da diversi fattori, ma il più importante è se avete il potere di modificarlo.

Nel caso in cui lo si fa, refactoring. Ad esempio, rinomina classi/funzioni/variabili. Estrai e generalizza le funzionalità. Vedi Refactoring: Improving the Design of Existing Code (Bibbia per il soggetto). Prima di iniziare, assicurarsi che il codice sia in un controllo di versione (VC) appropriato e disporre di un buon set di casi di test. VC ti consente di eseguire il rollback e testare i casi per ottenere effetti collaterali inattesi.

Suggerisco controllo di versione distribuito come Mercurial/Bazaar e Git perché è molto refactoring non esattamente strutturato come l'aggiunta di funzionalità.

Se non ci sono stati test (comuni), è necessario crearli. Leggi Working Effectively With Legacy Code. Soprattutto su "Seal point" (non sul gatto siamese: p).

Nel caso in cui non si crei un'API wrapper più pulita.

Ad esempio:


Old code ==================== 
const ACT_SHOW = 'show'; 
const ACT_HIDE = 'hide'; 
function int DoThing(Object $Obj, Stirng $Action, Object $Param1, Object $Param1) { 
    ....; 
} 
Added code ================== 
enum Actions { 
    show, hide; 
}; 
class ActionDoer { 
    private obj; 
    ActionDoer($Object) { 
     this.obj = $Object; 
    } 
    function int act(Actions $Action, $Param1, $Param1) { 
     this.act($Action.toString(), $Param1, $Param1) ; 
    } 
    function int act(String $Action, $Param1, $Param1) { 
     DoThing(this.obj, $Action, $Param1, $Param1) ; 
    } 
    function int show() { 
     this.act(Actions.show, null, null); 
    } 
    function int hide(Color $ToBGColor, long $FadeTime) { 
     this.act(Actions.hide, $ToBGColor, $FadeTime); 
    } 
} 

In questo modo, il vecchio codice non viene toccato e l'estensione può essere fatto utilizzando il nuovo codice. Un buon esempio di questo metodo è jQuery in cui il vecchio (predefinito) modo di accedere a DOM è doloroso.

Spero che questo aiuti.

1

Penso che sia sempre opportuno avere un'idea generale di come tutto funzioni nel software che si sta sviluppando/migliorando. È qui che entrano in gioco i documenti di progettazione e altri documenti realizzati dopo o durante il processo di sviluppo. Credo che se qualcuno prima di te non ha fatto la documentazione adeguata, almeno dovresti scrivere un paio di righe da qualche parte su ciò che sperimenti durante il tuo processo di sviluppo . Di solito uso OneNote o altre cose per scrivere note su ciò che incontro e di solito continuo a elencare le cose che ritengo richiederebbero il refactoring. E se durante il progetto ci sono dei tempi di inattività, di solito torno a quella lista e cerco di migliorare le cose un po 'alla volta.

Quindi, in sostanza, se qualcuno prima di te non lo ha fatto correttamente, sarebbe utile se almeno si potesse aiutare a ridurre i problemi per qualsiasi altro sviluppatore che avrebbe incontrato lo stesso codice.

3

Sono attualmente in questa situazione. Il mio approccio è quello di rispondere ad alcune domande prima di toccare il codice:

  1. è il codice davvero così male? Se sì, quali sono gli errori comuni? ==> forse concentrarsi su quelli primi
  2. Qual è il flusso di runtime principale nel codice? Forse puoi scartare un bel po 'di costrutti da esso.
  3. Provare a stratificare/modulare il codice senza cambiarlo. Ciò porta ad una certa riduzione delle interdipendenze
  4. Prova a inserire il codice con i test. Se la base di codice è impigliata oltre ogni speranza: utilizzare qualcosa come PowerMock per simulare oggetti che non hanno (ancora) bisogno di modifica
  5. Avere a disposizione un ambiente di integrazione, in cui è possibile verificare le modifiche in una produzione vicino all'ambiente.
  6. Non esitare a riscrivere parti del codice di base. Ma cercate di non implementare troppa roba nuova in esso
  7. Tenta di collaborare, discutere disegni, i principi, le soluzioni

Questo è un lavoro duro, e nessuno vi ringrazio per questo. Sii orgoglioso dei piccoli miglioramenti e goditi un buon lavoro :)

Problemi correlati