Domanda

Ho ereditato un progetto in cui i diagrammi di classe assomigliano molto a una ragnatela su un piatto di spaghetti. Ho scritto circa 300 test unitari negli ultimi due mesi per darmi una rete di sicurezza che copre l'eseguibile principale.

Ho la mia biblioteca di libri di sviluppo agile a portata di mano in qualsiasi momento:

  • Lavorare efficacemente con il codice legacy
  • refactoring
  • Codice completato
  • Schemi e pratiche dei principi agili in C #
  • ecc.

Il problema è che tutto ciò che tocco sembra rompere qualcos'altro. Le classi dell'interfaccia utente hanno una logica di business e un codice di database mescolati. Esistono dipendenze reciproche tra un numero di classi. Ci sono un paio di classi divine che si interrompono ogni volta che cambio una qualsiasi delle altre classi. Esiste anche una classe mutleton singleton / utility con circa i metodi metà istanza e metà metodi statici (sebbene ironicamente i metodi statici si basano sull'istanza e i metodi istanza no).

I miei predecessori hanno persino pensato che sarebbe stato intelligente usare tutti i set di dati al contrario. Ogni aggiornamento del database viene inviato direttamente al server db come parametri in una procedura memorizzata, quindi i set di dati vengono aggiornati manualmente in modo che l'interfaccia utente visualizzi le modifiche più recenti.

A volte sono tentato di pensare che abbiano usato qualche forma di offuscamento debole per la sicurezza del lavoro o come ultimo addio prima di consegnare il codice.

Esistono buone risorse per districare questo casino? I libri che ho sono utili ma sembrano coprire solo metà degli scenari in cui mi imbatto.

È stato utile?

Soluzione

Sembra che tu lo stia affrontando nel modo giusto.

  • Prova
  • Refactor
  • Riprova

Sfortunatamente, questo può essere un processo lento e noioso. Non c'è davvero alcun sostituto per scavare e comprendere ciò che il codice sta cercando di realizzare.

Un libro che posso consigliare (se non lo hai già archiviato in " ecc. ") è Refactoring su Patterns . È orientato verso le persone che si trovano nella tua situazione esatta.

Altri suggerimenti

Sto lavorando in una situazione simile.

Se non è una piccola utility ma un grande progetto aziendale, lo è:

a) troppo tardi per risolverlo
   b) oltre le capacità di una sola persona di tentare a)
   c) può essere risolto solo mediante una completa riscrittura del materiale fuori discussione

In molti casi il refactoring può essere tentato solo nel tuo tempo privato a tuo rischio personale. Se non hai un mandato esplicito per farlo come parte del tuo lavoro quotidiano, probabilmente non avrai nemmeno alcun credito per questo. Può anche essere criticato per "inutilmente sprecare tempo su qualcosa che ha funzionato perfettamente già da molto tempo".

Continua a modificarlo come è stato prima, ricevi la busta paga e così via. Quando sei completamente frustrato o il sistema raggiunge il punto di non essere più hackerabile, trova un altro lavoro.

EDIT: Ogni volta che tento di affrontare la questione della vera architettura e di fare le cose nel modo giusto di solito ricevo LOL in faccia direttamente dai responsabili responsabili che dicono qualcosa come " Non me ne frega niente del bene architettura " (tentativo di traduzione dal tedesco). Personalmente ho portato un componente pessimo al punto di non hackerabilità, mentre ovviamente avevo dato avvisi anticipati con mesi di anticipo. Hanno quindi dovuto annullare alcune funzionalità promesse ai clienti perché non era più possibile. Nessuno lo tocca più ...

Ho già svolto questo lavoro. Ho trascorso poco più di due anni in una bestia legacy molto simile. Ci sono voluti due di noi in un anno solo per stabilizzare tutto (è ancora rotto, ma è meglio).

Prima cosa: ottieni la registrazione delle eccezioni nell'app se non esiste già. Abbiamo utilizzato FogBugz e ci sono voluti circa un mese per integrare i rapporti nella nostra app; non è stato perfetto subito, ma ha segnalato automaticamente errori. Di solito è abbastanza sicuro implementare blocchi try-catch in tutti i tuoi eventi e questo coprirà la maggior parte dei tuoi errori.

Da lì, correggi i bug che arrivano per primi. Quindi combatti le piccole battaglie, in particolare quelle basate sugli insetti. Se correggi un bug che influisce in modo imprevisto su qualcos'altro, refactoring quel blocco in modo che sia disaccoppiato dal resto del codice.

Ci vorranno alcune misure estreme per riscrivere una grande applicazione critica per il successo dell'azienda, non importa quanto sia brutta. Anche se hai il permesso per farlo, passerai troppo tempo a supportare l'applicazione legacy per fare comunque progressi sulla riscrittura. Se fai molti piccoli refactoring, alla fine o quelli grandi non saranno così grandi o avrai delle ottime classi di base per la tua riscrittura.

Una cosa da togliere a questo è che è una grande esperienza. Sarà frustrante, ma imparerai molto.

Ho (una volta) trovato codice così follemente aggrovigliato che non sono riuscito a risolverlo con un duplicato funzionale in un ragionevole lasso di tempo. Questo è stato una specie di caso speciale, dato che era un parser e non avevo idea di quanti clienti potessero essere " usando " alcuni dei bug che aveva. Rendering di centinaia di "funzionamento" i file sorgente errati non erano una buona opzione.

Il più delle volte è imminentemente fattibile, solo scoraggiante. Leggi quel libro sul refactoring.

In genere comincio a correggere il codice errato spostando le cose un po '(senza in realtà cambiare il codice di implementazione più del necessario) in modo che i moduli e le classi siano almeno in qualche modo coerenti.

Al termine, puoi prendere la tua classe più coerente e riscriverne le viscere per eseguire esattamente lo stesso modo, ma questa volta con un codice sensibile. Questa è la parte difficile della gestione, poiché in genere non gli piace sapere che ci vorranno settimane per programmare e eseguire il debug di qualcosa che si comporterà esattamente allo stesso modo (se tutto va bene).

Durante questo processo ti garantisco che scoprirai tonnellate di bug e stupidezze progettuali. Va bene correggere bug banali durante la ricodifica, ma altrimenti lasciare tali cose per dopo.

Una volta fatto questo con un paio di classi, inizierai a vedere dove le cose possono essere modularizzate meglio, progettate meglio, ecc. Inoltre sarà più facile apportare tali modifiche senza influire su cose non correlate perché il codice ora è più modulare e probabilmente lo conosci a fondo.

Principalmente, suona piuttosto male. Ma non capisco questa parte:

  

I miei predecessori hanno persino pensato che sarebbe successo   essere intelligente per usare tutti i set di dati   indietro. Ogni aggiornamento del database è   inviato direttamente al server db come   parametri in una procedura memorizzata, quindi   i set di dati vengono aggiornati manualmente in questo modo   l'interfaccia utente visualizzerà il più recente   modifiche.

Sembra abbastanza vicino al modo in cui scrivo spesso cose. Cosa c'è di sbagliato in questo? Qual è il modo corretto?

Se i tuoi refactoring stanno infrangendo il codice, in particolare il codice che sembra non essere correlato, allora stai provando a fare troppo alla volta.

Raccomando un refactoring di primo passaggio in cui tutto ciò che fai è ExtractMethod: l'obiettivo è semplicemente nominare ogni passaggio del codice, senza alcun tentativo di consolidamento.

Dopodiché, pensa a spezzare le dipendenze, sostituire i singoli, il consolidamento.

Se i tuoi refactoring stanno rompendo le cose, significa che non hai un'adeguata copertura dei test unitari, poiché i test unitari avrebbero dovuto prima rompersi. Ti consiglio di ottenere una migliore copertura dei test unitari dopo aver ottenuto la registrazione delle eccezioni.

Ti consiglio quindi di fare prima dei piccoli refactoring - Estrai Metodo per spezzare metodi grandi in pezzi comprensibili; Introdurre Variable per rimuovere alcuni duplicati all'interno di un metodo; forse Introduce Parameter se trovi la duplicazione tra le variabili utilizzate dai tuoi chiamanti e il chiamante.

Ed esegui la suite di test unitari dopo ogni refactoring o set di refactoring. Direi di eseguirli tutti fino a quando non avrai la certezza di quali test dovranno essere rieseguiti ogni volta.

Nessun libro sarà in grado di coprire tutti i possibili scenari. Dipende anche da cosa dovresti fare con il progetto e se esiste qualche tipo di specifica esterna.

  • Se devi solo apportare piccole modifiche occasionali, fai solo quelle e non preoccuparti di iniziare il refactoring.
  • Se esiste una specifica (o puoi convincere qualcuno a scriverla), considera una riscrittura completa se può essere giustificata dalla quantità prevedibile di modifiche al progetto
  • Se " l'implementazione è la specifica " e ci sono molte modifiche pianificate, quindi sei praticamente esagerato. Scrivi MOLTI unit test e inizia il refactoring a piccoli passi.

In realtà, i test unitari avranno un valore inestimabile, qualunque cosa tu faccia (se riesci a scriverli su un'interfaccia che non cambierà molto con refactoring o riscrittura, cioè)

È possibile estrarre e quindi refactificare una parte di esso, per spezzare le dipendenze e isolare i livelli in diversi moduli, librerie, assiemi, directory. Quindi reinietti le parti pulite nell'applicazione con una strategia strangler . Raccogliere, sciacquare, ripetere.

Buona fortuna, questa è la parte difficile dell'essere uno sviluppatore.

Penso che il tuo approccio sia buono, ma devi concentrarti sulla fornitura di valore commerciale (il numero di unit test non è una misura del valore commerciale, ma può darti un'indicazione se sei on o off). È importante aver identificato i comportamenti che devono essere cambiati, dare priorità e concentrarsi su quelli migliori.

L'altro consiglio è di rimanere umili. Renditi conto che se scrivessi qualcosa di così grande con scadenze reali e qualcun altro vedesse il tuo codice, probabilmente avrebbero problemi anche a capirlo. Esiste un'abilità nello scrivere codice pulito e un'abilità più importante nel trattare il codice di altre persone.

L'ultimo consiglio è cercare di sfruttare il resto della tua squadra. I membri precedenti possono conoscere informazioni sul sistema che puoi imparare. Inoltre, potrebbero essere in grado di aiutare a testare i comportamenti. So che l'ideale è avere test automatici, ma se qualcuno può aiutarti verificando manualmente le cose, prendi in considerazione l'idea di ottenere il loro aiuto.

Mi piace particolarmente il diagramma in Codice completo , in che si inizia con solo il codice legacy, un rettangolo di texture grigio fuzzy. Quindi, quando ne sostituisci una parte, hai il grigio sfocato nella parte inferiore, il bianco solido nella parte superiore e una linea frastagliata che rappresenta l'interfaccia tra i due.

Cioè, tutto è o "cattive cose vecchie" o "belle cose nuove". Un lato della linea o l'altro.

La linea è frastagliata, perché stai migrando diverse parti del sistema a velocità diverse.

Mentre lavori, la linea frastagliata scende gradualmente, fino a quando non hai più bianco che grigio, e alla fine solo grigio.

Naturalmente, ciò non rende le specifiche più facili per te. Ma ti dà un modello che puoi usare per monitorare i tuoi progressi. In qualsiasi momento dovresti avere una chiara comprensione di dove sia la linea: quali bit sono nuovi, quali sono vecchi e come comunicano le due parti.

Potresti trovare utile il seguente messaggio: http://refactoringin.net/?p=36

Come si dice nel post, non scartare una sovrascrittura completa così facilmente. Inoltre, se possibile, prova a sostituire interi livelli o livelli con una soluzione di terze parti come ad esempio ORM per persistenza o con nuovo codice. Ma soprattutto, cerca di capire la logica (dominio problematico) dietro il codice.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top