2009-12-08 13 views
7

Diciamo che hai un progetto che è scritto male, contiene molti odori di codice, wtfs, ecc. Inoltre, la sua struttura di codice è così complicata che è estremamente difficile aggiungere nuove funzionalità ad esso. D'altra parte, il progetto funziona come dovrebbe.Refactoring di un progetto di lavoro

Si vuole rifattorizzare il progetto, magari spostarlo in un nuovo framework, come si affronterà questo problema? Proverai a crearne uno nuovo partendo da zero o userai alcune tecniche (specificare) per convertire il progetto di lavoro in uno nuovo?


vorrei chiarire la questione un po 'in quanto v'è una confusione che cosa intendo quando dice "refactoring".

Darò un esempio di un'auto, pensandoci come un progetto software. Diciamo che hai costruito la tua auto. La sua costruzione è piuttosto strana: il motore è capovolto, quindi tutti i tubi sono disposti in modo diverso, i cavi elettrici sono aggrovigliati e nessuno ha un'idea da dove iniziare o finire, ecc.

Tuttavia, tutto funziona bene: tu può facilmente guidarlo per fare acquisti, lavorare, ecc. Tuttavia, il suo consumo di carburante è un po 'troppo alto. Inoltre, se mai volessi installare nuovi fari, sarebbe un disastro con tutto il casino nei fili.

Non ti puoi permettere di acquistarne uno nuovo, quindi devi rifattorizzare l'auto in qualche modo: cambia la posizione dei motori in posizione normale, fai i fili in ordine, ecc. Hai bisogno di fare questo, perché prima o poi dovrai cambiare motore , fari, installa il nuovo stereo ecc. D'altra parte, hai ancora bisogno di qualcosa che ti spinga a lavorare ogni mattina, quindi devi assicurarti di non rovinare tutto.

Ora torniamo al progetto. Come giudicheresti il ​​progetto come complicato come l'auto sopra, senza disturbare la sua funzione e il suo scopo principale.


Vorrei anche fare di questo un wiki della comunità. Si prega di modificare.

Finora le principali tendenze sono:

vicini:

+0

Grazie per l'aggiornamento! Un'altra domanda che vorrei chiedere: quanto è importante non rompere la macchina? Quanti bug puoi introdurre e correggere prima che ti venga detto di smettere di fare cambiamenti? –

+0

beh, ne hai bisogno per funzionare come lo stai guidando il giorno successivo. Quindi dovresti sempre mantenerlo il più buono possibile. –

risposta

6

Lavorare è l'unico tipo di progetto che si dovrebbe essere refactoring. Se stai correggendo i bug, stai cambiando il comportamento, e il refactoring è esplicitamente su non sul comportamento di modifica. Ma funziona ha diverse definizioni. Quello buono, utile per il refactoring, è ben collaudato. Se hai una buona copertura di prova (test automatizzati!), Sei pronto per il refactoring. Se non ...

Leggi Michael Feathers 'Lavorare in modo efficace con il codice legacy. Sgranocchiare il codice Scegli un WTF che ti offende in particolare, e prova a morte, con test unitari automatizzati. Quindi sostituire il WTF con qualcosa di ragionevole, assicurandosi che i test continuino a passare. Mescolare, sciacquare, ripetere.

Refactor the low-hanging fruit.

+0

+1 per la distinzione. Sebbene il libro di Michael sia estremamente utile, tuttavia, noterei che non riconosce mai che è possibile, utile, a volte necessario, e talvolta anche perfettamente sicuro, rompere il martello e ristrutturare radicalmente un'applicazione in modo non caustico. –

5

In primo luogo, creare una suite di test di unità automatizzati, e confermare il possesso di copertura del codice elevata (70% o più). Durante il refactoring, eseguirai questi test molto frequentemente, al fine di convincere te stesso (e il Management) che non hai infranto nulla.

Nessun test di unità = nessun refactoring.


Fammi cambiare idea un po '. Non hai bisogno di test unitari - hai bisogno di una copertura di codice elevata da test che eseguirà frequentemente, mentre cambi il codice. Questi potrebbero essere test unitari automatizzati o test funzionali automatizzati.

Non credo che i test manuali siano un sostituto adeguato. È molto meno probabile che vengano eseguiti frequentemente durante il processo di refactoring. Il refactoring ha meno probabilità di successo senza la costante rassicurazione che il codice non è stato infranto nel processo di risoluzione.

+0

@Downvoter: "-2" non ha senso se non dici il perché. Se vuoi che il tuo voto abbia importanza, allora parla. –

+0

-1 Se si ritiene che la creazione di test automatici per un progetto di lavoro sia il primo passaggio, non è possibile avere mai rifatto un progetto di dimensioni significative. I test unitari non sono la prima cosa da fare con un sistema di dimensioni. Leggi "Lavorare efficacemente con il codice legacy" di Michael Feathers. –

+0

Ummm ... Attendi il commento prima di sollevare il reclamo? –

4

Questo vi darà una prospettiva sulla riscrittura da zero:

http://www.joelonsoftware.com/articles/fog0000000069.html

+0

Il punto di Joel è un business: perderai la quota di mercato riscrivendo. Questo argomento non si applica a vaste fette di software in natura. –

+0

Non ci sono grandi fette di software in circolazione dalle aziende? Quello che ho ottenuto dall'articolo è che riscrivere il software da zero è rischioso. La scrittura di software interno difettoso non può causare la perdita della quota di mercato, ma potresti perdere il lavoro. –

+0

@Andy: Potresti; più tipicamente il rischio di perdere il proprio lavoro deriva dall'implementare le funzionalità troppo lentamente, quindi qualcuno esce e compra un nuovo pacchetto. Riscrivere un'applicazione interna può spesso essere molto più veloce rispetto all'implementazione di una nuova importante funzionalità in un design doloroso. –

7

Si consiglia di leggere Working Effectively with Legacy Code.

+1

Wow, ora lo voglio! –

+1

Questo è un libro eccellente a fini di riferimento per rielaborare un sistema esistente. –

1

test unitari sono una buona cosa. Tuttavia, il codice deve essere in un determinato stato prima di poterlo persino testare. Scommetto che hai una convalida dei dati che parla con il livello di persistenza all'interno delle funzioni di gestione dei messaggi. Sono sicuro che hai migliaia di linee di business logic che interrompono l'utente con caselle di messaggio di conferma.

È necessario separare la business logic dal livello dell'interfaccia utente e questo può richiedere molto lavoro.

Ora hai una classica situazione di Catch-22 - non devi refactoring senza copertura del test unitario, e non puoi scrivere test unitari fino a quando non viene refactored.

Quindi l'unica soluzione è essere molto pazienti. Capire esattamente come dovrebbe funzionare l'applicazione. Cerca di capire cosa sta facendo ogni pezzo di codice. Accetta il fatto che a volte non sarai in grado di dare un senso a qualche codice, e la ragione di ciò è che il codice in realtà non ha senso. Ma il codice inutile apparirà esattamente come il codice fondamentalmente utile, e solo un duro lavoro estremo ti darà l'intuizione di distinguere la differenza.

Lavorare verso l'obiettivo di avere una copertura di prova unitaria - ma la mancanza di test unitari non dovrebbe smettere di iniziare. Accetta il fatto che potresti non ottenere mai la copertura del test dell'unità che desideri, ma tieni a mente l'ideale.

E ogni giorno dirai a te stesso "Sarebbe molto più veloce se avessi riscritto il tutto". NON AGIRE SU QUESTA SENTENZA. (Vedi l'articolo di Joel, già menzionato).

+0

-1: non sono assolutamente d'accordo. Il codice non dovrebbe essere toccato fino a che non esistano test unitari per dimostrare che toccarlo non sta rompendo qualcosa. Altrimenti, toccandolo, anche con le migliori intenzioni, _will_ rompere qualcosa, e ti costa la buona volontà di gestione. –

+0

+1 Ho esposto il caso in modo più dettagliato, ma questo è ciò di cui sto parlando. I test unitari sono inutili se in realtà non si hanno unità con comportamento previsto in alcun senso significativo. –

2

C'è un sacco di testo in risposta a questa domanda che segue la letteratura molto da vicino e (senza offesa per i poster di quelle risposte) mi chiedo se le persone semplicemente non hanno rielaborato un sistema di qualsiasi scala significativa questo è veramente e veramente fottuto.

Ho riscritto tre applicazioni significative nella mia carriera (Significativo: 50kish LOC o superiore, livello dati, livello logico, requisiti di integrazione). Ogni sistema mi ha richiesto di dire all'inferno con questo a un certo punto di buone pratiche. Sai cosa ho imparato dal farlo? Ad un certo punto, può essere davvero, davvero sicuro. Assolutamente, devi considerare cosa significa lanciare cautela al vento, ma è molto più importante spedire che seguire l'idea di buona pratica di qualcun altro.

Permettetemi di illustrare con un esempio di che cosa sto parlando:

oggi sto lavorando su un sistema che è stato scritto più di sei anni, che è stato convertito in un linguaggio .NET da un'applicazione che è stata all'epoca forse un decennio, e fu scritto con un client DOS che conteneva tutta la logica. Il codice originale e la maggior parte degli aggiornamenti e conversioni successivi sono stati gestiti da programmatori inesperti. Questo è un motore di gestione dei documenti a tutti gli effetti, e non c'è una sola astrazione per "documento" in qualsiasi parte del codice.

Mi è stato chiesto di implementare un metodo per trasferire i file attraverso una WAN in modo che funzionasse con le routine principali del sistema. Ho iniziato a creare un buon piccolo client e server di test con pratiche decenti avvolti attorno a loro, test, ecc. Sono passato attraverso l'architettura del sistema centrale e ho cercato il posto giusto per re-stub il mio codice. Tutto quello che ho trovato è stato un grosso pezzo dopo un grosso pezzo di copia e codice incollato con piccole modifiche che costituivano unità.

Ho iniziato a cambiare piccoli pezzi di codice, le cose hanno iniziato a rompersi, ho ripristinato le mie modifiche. Ho estratto i metodi e ho scoperto che le variabili erano sparse per tutto il codice e facevano affidamento sulle modifiche apportate durante la procedura. Ho provato a estrarre le classi, solo per scoprire che stavo estraendo metodi all'ingrosso e che i dati di stato della vecchia classe erano, ancora una volta, sparsi per tutto il codice e non suscettibili di transizione.

Così ho detto THWI. Due settimane più tardi abbiamo avuto un piccolo client e server zippato e il nostro codice base è stato trattato in modo molto più accurato per il nostro problema.

Se si conosce un sistema, se si presta attenzione, se si esegue il test del codice e se si presta attenzione, spesso non c'è nulla di sbagliato nel fare grandi cambiamenti in un modo non sicuro.

I'm downvotes per questo, ma le pratiche Agile hanno offuscato troppo la visione della gente, ed è davvero il momento per le citazioni dalla letteratura di smettere di dominare queste discussioni. Se lavori con un sistema ogni giorno, conosci il sistema, e nessun test unitario equivale a quello che puoi fare se prendi il comando e aggiusti il ​​maledetto sistema.

Naturalmente, questo è il motivo per cui è necessario lavorare con un sistema e imparare il sistema anziché passare semplicemente dalla tecnologia alla tecnologia. Riscrivere in una nuova lingua non è una buona risposta come ricostruire con un occhio per migliorare il design. Se poi trovi che ci sono dei limiti nel sistema che potresti superare con una nuova tecnologia, diventa sostanzialmente più semplice apportare quel cambiamento a quel punto.

+1

@Mike: congratulazioni, ma quello che stai facendo è più "rielaborazione" o "riscrittura" e meno "refactoring". Sei persino in grado di misurare il numero di bug che stai introducendo in questo sistema? A proposito, le mie risposte non hanno nulla a che fare con "agile". I test unitari sono l'unica cosa che dimostra che le tue buone intenzioni modificate in codice errato non stanno peggiorando le cose. –

+0

+1 dalla fazione "pragmatismo vs idealismo". Questo si sta trasformando in un interessante dibattito. –

+0

Nota che non sto dicendo che il codice non può essere modificato. Sto dicendo che tali cambiamenti non sono il refactoring. Sono la stessa cosa che facevamo una volta il giorno prima che il libro di Fowler rendesse popolare il termine. In effetti, è possibile cambiare codice senza test di unità, se la tua suite di test funzionale è abbastanza buona. Ma quante volte hai intenzione di eseguire i tuoi test funzionali? Quanti cambiamenti saranno stati fatti prima che ti rendessi conto di aver rotto qualcosa qualche settimana fa? –

1

Preferisco il refactoring piuttosto che la riscrittura laddove possibile, perché è meno rischioso. Se fatto bene, avrai sempre un programma funzionante, e gradualmente migliorerà man mano che apporterai modifiche incrementali.Se devi fermarti presto (a causa di limiti di tempo o di budget), ottieni comunque il beneficio del lavoro svolto finora, perché il programma è ancora utilizzabile ed è migliore di prima, anche se non è ancora perfetto.

D'altra parte, se si sceglie di riscrivere e si deve interrompere parzialmente, si rimane con il programma originale funzionante ma male e il nuovo programma pulito ma incompleto. Dovrai decidere se scartare la nuova versione e lasciare che lo sforzo vada sprecato, o sacrificare la funzionalità e ottenere bug passando ad esso prima che sia pronto.

1

Innanzitutto, concordo con gli altri poster: non cedere alla tentazione di riscrivere da zero. Il codice funziona, dopo tutto. Secondo, potrebbe sembrare ovvio, ma concentrare il refactoring su quelle aree che necessitano di modifiche (per nuove funzionalità, ecc.). Potrebbe esserci un codice che chiede di essere refactored, ma se funziona e in una parte stabile dell'app, lascia stare.

lavorare efficacemente con Legacy Codice fa un grande lavoro di affrontare il comma 22 che non si desidera di refactoring, senza test di unità, ma non è possibile introdurre test di unità, senza refactoring. Tra le altre cose, suggerisce di basarsi su strumenti di refactoring automatici per evitare di introdurre bug durante il refactoring senza test.

Altri potrebbero non essere d'accordo con me, ma IMO a volte non si ha altra alternativa che eseguire il refactoring iniziale senza test di unità e fare affidamento su test di integrazione manuali automatizzati o (attenti) per assicurarsi di aver capito bene. Il refactoring iniziale dovrebbe consentire l'esecuzione del codice in un'unità di test unitaria, rispetto a quando sei sulla buona strada.

Problemi correlati