Come refactoring quando tutto il tuo sviluppo è sui rami?

21

Nella mia azienda, tutto il nostro sviluppo (correzioni di bug e nuove funzionalità) viene eseguito su filiali separate. Quando è completo, lo inviamo al QA che lo controlla su quel ramo e quando ci danno il via libera, lo uniamo al nostro ramo principale. Questo potrebbe richiedere tra un giorno e un anno.

Se proviamo a spremere qualsiasi refactoring su un ramo, non sappiamo per quanto tempo sarà "out", quindi può causare molti conflitti quando viene unito nuovamente.

Ad esempio, supponiamo di voler rinominare una funzione perché la funzione su cui sto lavorando sfrutta pesantemente questa funzione e ho scoperto che il suo nome non si adatta perfettamente al suo scopo (di nuovo, questo è solo un esempio). Quindi vado in giro e trovo ogni utilizzo di questa funzione, e rinominale tutti con il suo nuovo nome, e tutto funziona perfettamente, quindi lo invio al QA.

Nel frattempo, sta accadendo un nuovo sviluppo, e la mia funzione rinominata non esiste su nessuno dei rami che vengono biforcati fuori dalla main. Quando il mio problema viene riunito, si interromperanno tutti.

C'è un modo per affrontare questo?

Non è che la direzione approverà mai un problema di refactoring, quindi deve essere spremuto con altri lavori. Non può essere sviluppato direttamente su main perché tutte le modifiche devono passare attraverso il QA e nessuno vuole essere il jerk che ha rotto main in modo da poter fare un po 'di refactoring non essenziale.

    
posta mpen 16.10.2013 - 22:55
fonte

5 risposte

12

Ci sono diversi problemi che si mescolano per rendere difficile il refactoring in questo ambiente. In questo vi sono alcuni problemi non tecnici ("ma questo è un problema di gestione e una battaglia che non ho ancora vinto").

Il primo problema da considerare è il ramo a lungo termine. Questi rami hanno difficoltà a tenere traccia delle modifiche al di fuori della vista dello sviluppatore. Per risolvere questo problema:

  • Una volta completato il codice, assegnalo una volta (lascia che il supporto clienti lo guardi se lo desidera), ma uniscilo rapidamente in modo che altre modifiche che dipendono da esso possano essere recuperate e le modifiche siano in conflitto sono identificati all'inizio del processo.
  • Se, per qualche ragione, un ramo diventa di lunga durata mentre il refactoring sta succedendo, tende ad essere una buona pratica per fondersi da stabile nel ramo per raccogliere le modifiche e il refactoring. Spesso questo riduce al minimo i conflitti e le sorprese nell'unione dal ramo della caratteristica nel ramo stabile.
  • Tutti i test di integrazione devono essere eseguiti su releases - non funzioni . In questo ambiente le funzionalità potrebbero non essere completamente integrate con il sistema. Sebbene sia possibile eseguire un controllo di integrità sulla funzionalità separatamente, non identifica i problemi relativi al rilascio.
  • Dal momento del completamento del codice per unire a (chiamiamolo sviluppare - il diramazione da master / stable / release ha i suoi problemi di non raccogliere le ultime modifiche allo sviluppo) non dovrebbe essere troppo lungo. Più a lungo aspetti, più conoscenze vengono perse e più è difficile che il codice venga integrato con altre linee di codice.

Un altro problema che si sta mescolando in questo è che ho accennato con i punti di cui sopra è il cambiamento del ruolo del ramo nel tempo. Inizia come un ramo di sviluppo in cui gli sviluppatori si impegnano, e quindi diventa un'area di test (quale test viene fatto qui che può essere significativo in tutta l'applicazione?), Che viene poi fuso in stabile (e presumibilmente rilasciato - è testato di nuovo?).

Con una funzione più breve dall'inizio alla fine è più facile per il refactoring essere in grado di essere prelevato da altri rami.

Incoraggia gli sviluppatori a ottenere l'intero ambiente. Solo i cambiamenti di cherry-picking possono portare a ... diciamo interessanti ambienti di sviluppo. Mentre il cherry-picking ha i suoi usi, perché quello di essere la modalità predefinita di tirare le modifiche in un ramo può essere preoccupante.

Il refactoring è qualcosa che idealmente viene fatto costantemente o, se non costantemente, ogni volta che c'è un minimo di downtime. Ramo, esegui un semplice refactoring, esegui i test unitari per verificare che tutto funzioni ancora (la sua unità è stata testata, giusto? giusto? ) e quindi unisci nuovamente in stable. Passa le informazioni per altri sviluppatori per estrarre quelle modifiche che hai refactored nelle loro filiali.

Per gli sviluppatori è importante possedere la qualità del codice. Mentre la direzione delle funzionalità viene dall'esterno e le allocazioni temporali spesso non sono le nostre, la qualità del codice è qualcosa di cui è necessario essere orgogliosi e trovare il tempo necessario.

Potresti trovare le seguenti domande utili nella ricerca di allocare il tempo per affrontare il debito tecnico:

Potresti anche voler esaminare strumenti come sonar che possono aiutare a identificare le aree del codice che richiedono il maggior lavoro per il refactoring . Il plug-in del debito tecnico è qualcosa che può essere utilizzato per aiutare a sottolineare l'accumulo di debiti nel tempo nella base del codice.

Spesso è necessario sottolineare che il ROI per la gestione del debito tecnico è un tempo di consegna più rapido per le funzionalità e le correzioni dei bug del team di sviluppo.

    
risposta data 17.10.2013 - 01:48
fonte
3

Di solito sto sviluppando la versione refactored in "parallelo" con la corrente, vale a dire nella stessa base di codice, ma non facendo riferimento ad essa dall'applicazione principale. E quando una nuova soluzione viene eseguita e testata, sto iniziando un vero e proprio refactoring.

Esempio 1. Supponiamo di avere Thing, sia funzione, interfaccia, modulo o altro. E voglio refactoring. Sto creando Thing2 nella stessa base di codice, è la versione refactored di Thing. Quando è fatto e testato, sto refactoring tutto ciò che fa riferimento a Thing, per sostituirlo con Thing2. Di solito questo passaggio richiede un tempo relativamente breve.

Se il refactoring effettivo richiede troppo tempo per rimanere in sincrono senza rovinare la squadra, sto prendendo tutte le funzionalità pertinenti e le refactoring in parallelo, anche.

Esempio 2. Ho un nuovo backend di rendering, ovvero una versione refactored di quella precedente. Ma non è compatibile con il vecchio frontend di rendering. Quindi, ho bisogno di rifattorizzare frontend. E ancora: nella stessa base di codice. Quando tutto è fatto, sto solo cambiando la classe dell'istanza di frontend, idealmente ci vorrà un breve commit.

Sì, in modo ricorsivo si può concludere che tutto deve essere fatto in parallelo. Ma questo di solito accade quando c'è troppo accoppiamento nella base di codice, o sta cambiando troppo velocemente.

Infine, quando il nuovo codice è integrato e funziona bene, le vecchie funzionalità possono essere rimosse dalla base di codice e le nuove funzionalità possono essere rinominate per ottenere nomi vecchi.

Generalmente, l'idea è di preparare nuove funzionalità in parallelo e passare a usarle con un piccolo passo.

John Carmack usa questo approccio (o almeno simile), forse il suo post sul blog lo spiega meglio: (link)

    
risposta data 17.10.2013 - 13:24
fonte
1

Può sembrare una difficoltà dal lato tecnico quando in realtà è nel lato dei requisiti.

Dove lo sviluppo è orientato verso diverse esigenze in diversi rami è la vera difficoltà. I manager e gli architetti del team dovrebbero prendere decisioni che possano consentire alle diverse esigenze aziendali di coesistere.

Il processo ZBB e il "compromesso" di Co-Dev "dopo aver preso le giuste decisioni con gli input rilevanti di tutti gli sviluppatori dovrebbero afterwords abilitare l'implementazione di ciò che ti serve senza dover pensare - Come faccio a unire il mio codice.

ZBB sta per bilancio a base zero . Dicendo Co-Dev intendevo poche persone che lavorano nella programmazione parallela.

    
risposta data 16.10.2013 - 23:46
fonte
1

Il problema mi sembra che tu stia lavorando troppo a lungo sui rami. Il costo dei conflitti cresce in modo esponenziale con la lunghezza che tutti rimangono su un ramo, quindi con conflitti molto lunghi si hanno poche possibilità di fare qualsiasi refactoring.

    
risposta data 04.05.2016 - 18:43
fonte
0

Il tuo problema è il modello di filiale che stai utilizzando. Potresti svilupparti su un ramo e, una volta completato e pronto per il controllo qualità, il ramo viene unito a un "tronco intermedio", a volte chiamato integrazione o test. Quando sviluppi la funzione successiva, puoi invece diramarti da questo trunk intermedio.

Questo modello consente di sviluppare più funzionalità in parallelo su rami diversi, unendoli tutti insieme sul ramo di integrazione da inviare al QA e mantenendo anche un singolo trunk di release (unisci il codebase QA ricevuto al trunk principale quando lo certificano)

Si presuppone che le modifiche apportate al QA vengano passate senza modifiche importanti - se il codice QA viene restituito con le istruzioni per rimuovere metà delle modifiche, sarà necessario ripristinarlo, ma se ciò non accade Renderò il tuo sviluppo molto più fluido. Quindi stai sostanzialmente prendendo le ramificazioni per le nuove funzionalità da quello che sarà il tuo codice mainline (ad es. Trunk dopo l'unione nel codice passato a QA), invece di quello che è oggi (cioè il trunk corrente) e quindi non si sviluppa più rispetto al codebase della release precedente .

    
risposta data 04.05.2016 - 15:39
fonte

Leggi altre domande sui tag