Come prepararsi per riscrivere la colla di un'applicazione [chiusa]

5

Supponete di avere un progetto che (secondo la vostra opinione) è per lo più decentemente ben scritto, modularizzato, ecc. e volete mantenere gran parte della sua funzionalità. Tuttavia, una parte essenziale di questo progetto non è ben progettata e non può essere facilmente risolta con modifiche minori. L'unico modo per risolverlo è strappare un sacco di codice; non è così male che hai bisogno (o dovresti) di iniziare da zero, ma è molto pervasivo.

Supponiamo che il codice sia qualcosa come 20.000 righe di non-spazio bianco, non-singole parentesi graffe, non-comment Java, C ++, C #, codice PHP o Ruby. Si stima che circa 2000-3000 linee debbano essere eliminate o modificate. Nel caso di qualcosa come Java o C #, uno dei tuoi pacchetti / spazi dei nomi sarà probabilmente interamente riscritto da zero e molti dei riferimenti a tali classi cambieranno.

Ecco la parte più impegnativa: il codice che doveva cambiare era la COLLA nel sistema. Ecco un'immagine molto semplice che riassume il problema:

Questaèlasituazioneincuisonostatocondottoaquestadomanda: Cosa devo fare quando ho aspettato troppo a lungo tra commit?

Come puoi vedere, non sono solo le parti del codice che stavano eseguendo l'elaborazione interna, ma anche le parti che stavano comunicando con le altre parti del sistema. Sia le API che le strutture dati interne erano insufficienti. Il tentativo di mantenere entrambi i set di API funzionanti per modifiche incrementali sarebbe stato ridondante.

Naturalmente, mi sono ramificato prima di fare qualsiasi cosa, ma ogni cambiamento che ho apportato ha prodotto un prodotto che semplicemente non funzionava. In molti casi ho creato errori di compilazione. Questo mi ha portato a rimandare continuamente i commit, perché pensavo "sì, ho ricollegato questi pezzi insieme, ma ho ancora bisogno di implementare questa altra nuova interfaccia, scrivere questa funzionalità essenziale, ecc." Ciò ha comportato il peccato capitale di non controllare nulla, un errore che preferirei non ripetere se dovessi essere di nuovo in questa situazione. Ciò è stato mitigato dal fatto che tutte le mie API erano interne, tranne che dovevo conformarmi alle API del mondo esterno e ai moduli di terze parti, ma i miei wrapper per quelle sezioni stavano cambiando.

Una soluzione alternativa ovvia sarebbe accettare semplicemente il fatto che ogni modifica comporterà un prodotto complessivo rotto, ma dal momento che si sta verificando un ramo, va bene. Soprattutto perché sono l'unico sviluppatore che lavora su questo codice e usa queste API. Quindi forse questa è la cosa giusta da fare. O forse la cosa giusta da fare è provare a ridefinire prima l'API a quella nuova, senza effettivamente modificare la funzionalità (se possibile), anche se alcuni argomenti sono fittizi o saranno eventualmente rimossi, e quindi le modifiche incrementali sono molto più utili contenute. O forse c'è un modo migliore del tutto?

    
posta durron597 27.02.2014 - 20:14
fonte

3 risposte

8

In parole semplici: Non bollire l'oceano .

In altre parole, rompere blocchi piccoli e gestibili invece di provare a fare tutto in una volta. In un ramo. Senza controlli regolari.

Mi è sempre piaciuto questo articolo su Joel on Software . Fornisce ottimi esempi concreti sul tipo di piccoli cambiamenti che possono essere apportati contro un codice di base per renderlo più pulito a poco a poco.

Stabilisci una parte concettualmente simile di modifiche e crea un ramo per questo. Un esempio potrebbe essere lo spostamento di tutto il codice SQL in classi. In quel ramo, il tuo flusso di lavoro sarebbe simile a questo:

  • Rimuovi una dichiarazione SQL
  • Esegui i tuoi test
  • Controlla il tuo codice
  • Ripeti

Una volta completato questo blocco di modifiche, uniscilo nel tuo ramo principale e inizia un nuovo blocco.

Questo presuppone che tu abbia dei test. In caso contrario, è necessario crearli prima di iniziare (compresi i test di unità, regressione e integrazione). Di nuovo, non tutto in una volta. Se hai una funzione che vuoi refactoring per cui non si dispone di test, scrivere i test, refactoring il codice ed eseguire i test fino a quando non passano. Questo è esattamente ciò per cui sono costruiti questi test. Assicurati di utilizzarli.

    
risposta data 27.02.2014 - 23:14
fonte
2

È importante sapere se il tuo prodotto è stato rilasciato o meno e se l'API che intendi toccare è esposta in qualche modo ad altre entità (moduli, progetti, prodotti, documentazione, ecc.) che vengono rilasciati separatamente. Se la risposta a una di queste è "sì", allora deve preservare la compatibilità delle API nella fase di transizione. Invece di sostituire la vecchia API, estenderla. Nella seconda fase, è possibile rendere obsoleta la vecchia API e consentire alle entità interessate di aggiornarsi. Se puoi verificare che tutte le entità pertinenti abbiano smesso di utilizzare la vecchia API, solo allora puoi rimuoverla dalla tua parte. (Anche se non hai dipendenze esterne, potrebbe comunque aiutare la riscrittura a utilizzare lo stesso metodo).

Detto questo, un buon approccio a una grande riscrittura è dividere il lavoro in piccoli passi indipendenti, il più possibile.

È facile cadere nella trappola di lavorare simultaneamente su più obiettivi, ma di solito ingarbuglia i tuoi commit in un modo o nell'altro - nel migliore dei casi, dovrai tenere traccia di più rami e caratteristiche progressive, che dovranno essere unite a un certo punto; Nel peggiore dei casi, tutte le nuove funzionalità sono sviluppate in modo intercalato l'una con l'altra, impedendo di eseguire committenti puliti appartenenti a una singola funzione, o addirittura impedendoti di commettere alcun commit, perché continui a disporre di funzionalità semipiegate o non di compilazione. Inoltre, "ti aiuterà" a introdurre tutti i tipi di oscuri bug di interazione, fornendo molti problemi quando cerchi di trovare il punto esatto per introdurre il bug.

Questo è in gran parte un esercizio di disciplina mentale, e c'è più di un approccio ad esso. Potresti iniziare estraendo la nuova API e documentando lo scopo previsto e i dettagli dell'API. Una volta fatto, hai un punto di commit stabile. Quindi, è possibile passare all'implementazione di singole funzionalità e test di unità (e si è in grado di impegnarsi più volte lungo la strada). Infine, modifichi i moduli che chiamano la tua nuova API e fai tutto il lavoro di "colla". Questo ordine ti aiuterà ad avere punti di arresto in cui puoi effettuare il commit senza interrompere la build.

Ancora una volta, questo è solo un modo, non l'unico modo. Il tuo scopo è modulare la riscrittura e ridurre al minimo le interazioni tra le modifiche.

    
risposta data 27.02.2014 - 23:13
fonte
2

Sono stato coinvolto in più di una mirata riscrittura. Prima di iniziare a scrivere altri codici devi fare delle analisi:

  • Cosa c'è di diverso dai pezzi ben progettati e dai pezzi mal progettati?
  • È possibile rielaborare il codice mal progettato in modo che corrisponda alla stessa struttura di progettazione del buon codice?
  • Identifica tutti i punti di contatto con il codice mal progettato. Cioè quali sono i metodi chiamati, come vengono chiamati (cioè istanze di oggetti o singoletti, ecc.)
  • Quanti di questi punti di contatto puoi eliminare in sicurezza?
  • Identifica le interfacce che puoi utilizzare per isolare l'esposizione del codice errato dal codice corretto.

Da tutti quei dati grezzi, pensa davvero a come dovrebbe essere il tuo risultato finale. Devi sapere quando hai finito, o quando la riscrittura è abbastanza buona da darti un prodotto di qualità senza la necessità di scartare ogni singola riga di codice.

  • Suddividi il lavoro in attività che puoi sviare uno alla volta.
  • Stima la durata di ciascuna attività. Se non si riesce a fornire una stima valida, è probabile che l'attività debba essere ulteriormente suddivisa. Includere il tempo per i test automatici e / o unitari.
  • Sommati tutto il tempo che pensi che ogni attività che farai e triplicherai. Sul serio. Lì saranno conseguenze nascoste della tua riprogettazione che non hai modo di anticipare a questo punto.
  • Limita le tue attività solo alla riscrittura mirata. Non hai tempo per nuove funzionalità e non hai tempo per lavorare su altre parti del codice.
  • Esegui i tuoi compiti uno alla volta. Assicurati che ogni attività sia completa e testata prima di proseguire.

Posso dire, dopo le mie esperienze con riscritture mirate, che molte volte il prodotto risultante era migliore e più facile da mantenere, ma il problema principale era il programma. Se hai tempo limitato per fare questo lavoro, una riscrittura renderà il tuo progetto molto più tardi di quanto pensi inizialmente.

    
risposta data 27.02.2014 - 23:40
fonte