Come refactoring quando tutto il tuo sviluppo è sulle filiali?


24

Nella mia azienda, tutto il nostro sviluppo (correzioni di bug e nuove funzionalità) viene eseguito su rami separati. Quando è completo, lo inviamo al QA che lo verifica su quel ramo e quando ci danno la luce verde, lo uniamo nel 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à "fuori", quindi può causare molti conflitti quando viene ricongiunto.

Ad esempio, diciamo che voglio rinominare una funzione perché la funzione su cui sto lavorando sta facendo un uso pesante di questa funzione e ho scoperto che il suo nome non si adatta al suo scopo (di nuovo, questo è solo un esempio). Quindi vado in giro e trovo ogni utilizzo di questa funzione, e li rinomino tutti con il suo nuovo nome e tutto funziona perfettamente, quindi lo invio al QA.

Nel frattempo, sta avvenendo un nuovo sviluppo e la mia funzione di ridenominazione non esiste in nessuno dei rami che sono stati biforcati. Quando il mio problema verrà nuovamente unito, si romperanno tutti.

C'è un modo di affrontare questo?

Non è come se la direzione approvasse mai un problema solo per i refactor, quindi deve essere compreso con altri lavori. Non può essere sviluppato direttamente sul main perché tutti i cambiamenti devono passare attraverso il QA e nessuno vuole essere il coglione che ha rotto il main in modo da poter fare un po 'di refactoring non essenziale.


Quale controllo di versione stai usando? Esistono diversi approcci per DVCS e un modello di server centralizzato. Inoltre, da cosa vengono tolti i rami di sviluppo? Se viene accettato un ramo di funzionalità, in che modo gli altri rami di sviluppo rilevano le modifiche?

2
A parte questo, un diagramma dell'attuale struttura ramificata potrebbe essere davvero utile. È del tutto possibile che la radice del problema con la difficoltà con il refactoring sia in parte causata da alcune ... politiche di branching non convenzionali (vedi programmers.stackexchange.com/questions/210360 per uno di questi esempi). Vorrei anche suggerire di leggere vance.com/steve/perforce/Branching_Strategies.html per ottenere alcune idee e informazioni (se sono in grado di rispondere a questa domanda che sarà un punto di riferimento importante).

1
L'ultimo paragrafo lo riassume: se il Business non percepisce il valore, non è possibile che un importante refactor possa andare avanti. È necessario collaborare con il team di test per risolvere i tempi. (Sospetto che il tuo QA sia davvero Test in resistenza (hanno messo una parrucca e un rossetto e fingono di essere qualcosa che non sono). Un vero team di QA ti direbbe cosa fare per refactoring, non
ostacolarti

1
@mattnz: hai perfettamente ragione. Non sono un vero team di controllo qualità. Sono principalmente assistenza clienti. Penso che molte delle loro responsabilità dovrebbero essere ricondotte al team Dev perché semplicemente non riescono a gestire tutto ciò che scarichiamo su di loro, ma questo è un problema di gestione e una battaglia che devo ancora vincere.
mpen

3
Ti sei perso il mio scavo. Test! = QA. Il controllo qualità controlla la qualità e mira a migliorare i risultati aziendali. Test tenta di provare l'assenza di difetti trovandoli.
mattnz,

Risposte:


12

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

Il primo problema da considerare è il ramo di lunga data. Queste filiali hanno difficoltà a tenere traccia delle modifiche al di fuori della vista dello sviluppatore. Per affrontare questo:

  • Quando il codice è completo, dagli una volta (lascia che il supporto del cliente lo guardi se lo desidera), ma uniscilo rapidamente allo sviluppo in modo che altri cambiamenti che dipendono da esso saranno in grado di essere colti e le modifiche che il conflitto sono identificate in anticipo nel processo.
  • Se, per qualche motivo, un brach diventa long run mentre il refactoring è in corso, tende ad essere una buona pratica fondersi da stabile nel ramo per raccogliere cambiamenti e refactoring. Spesso ciò riduce al minimo i conflitti e le sorprese durante l'unione dal ramo delle caratteristiche al ramo stabile.
  • Tutti i test di integrazione devono essere eseguiti sulle versioni , non sulle funzionalità . In questo ambiente le funzionalità possono o meno essere completamente integrate con il sistema. Sebbene sia possibile effettuare un controllo di integrità sulla funzionalità in modo isolato, non identifica i problemi al rilascio.
  • Dal momento del completamento del codice da unire a (chiamiamolo sviluppo - la ramificazione da master / stable / release ha i suoi problemi di non raccogliere le ultime modifiche di sviluppo) non dovrebbe essere troppo lungo. Più a lungo aspetti, maggiore è la conoscenza perduta e più difficile è l'integrazione del codice con altre righe di codice.

Un altro problema che si sta mescolando a questo è che ho accennato con i punti di cui sopra è il ruolo mutevole del ramo nel tempo. Inizia come un ramo di sviluppo in cui gli sviluppatori si impegnano, e quindi diventa un'area di test (quali test vengono eseguiti qui che possono essere significativi in ​​tutta l'applicazione?), Che viene poi unito in stabile (e presumibilmente rilasciato - è testato di nuovo?).

Con una funzione più breve, dall'inizio alla fine, è più facile per il refactoring essere raccolto da altri rami.

Incoraggia gli sviluppatori a ottenere l'intero ambiente. Solo cambiamenti nella selezione delle ciliegie possono portare a ... diciamo ambienti di sviluppo interessanti. Mentre la raccolta delle ciliegie ha i suoi usi, perché quella è la modalità predefinita di trascinare le modifiche in un ramo può essere preoccupante.

Il refactoring è qualcosa che idealmente viene eseguito costantemente, o se non costantemente ogni volta che c'è un brusco tempo morto. Diramazione, esegui un semplice refactoring, esegui i test delle unità per verificare che tutto funzioni ancora (la sua unità è stata testata, giusto? Giusto? ) E poi ritorna in stalla. Passa in giro le informazioni per gli altri sviluppatori per inserire quelle modifiche che hai refactored nelle loro filiali.

È importante che gli sviluppatori possiedano la qualità del codice. Mentre la direzione delle funzionalità viene dall'esterno e le allocazioni di tempo spesso non sono le nostre, la qualità del codice è qualcosa di cui è necessario essere orgogliosi e prendere tempo.

Potresti trovare utili le seguenti domande nella ricerca di allocazione del tempo per far fronte al debito tecnico:

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

Spesso è necessario sottolineare che il ROI per far fronte al debito tecnico è un tempo di risposta più rapido per le funzionalità e le correzioni di errori del team di sviluppo.


I test vengono eseguiti essenzialmente in tre punti nel tempo. Una volta quando il problema è stato risolto risolto (per assicurarsi che soddisfi tutti i requisiti e non ci siano problemi importanti), ancora una volta quando viene ricondotto in default (test di integrazione), e ancora quando facciamo una build (integrazione con tutti i ciliegi scelti problemi / aspetto finale). Penso che la raccolta delle ciliegie sia necessaria nel nostro ambiente poiché gestiamo una SaaS con clienti molto particolari. Dò un'occhiata a questi link, grazie per i puntatori! Modifica: In realtà c'è un altro look-over sulla produzione per assicurarsi che sia andato bene.
Aprire il

3

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

Esempio 1. Supponiamo che io abbia Cosa, lascia che sia funzione, interfaccia, modulo o altro. E voglio riformattarlo. 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 relativamente poco tempo.

Se il refactoring effettivo impiega troppo tempo per rimanere sincronizzato senza avvitare il team, sto prendendo tutte le funzionalità pertinenti e le refactoring anche in parallelo.

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

Sì, ricorsivamente si può arrivare alla conclusione che tutto deve essere fatto in parallelo. Ma questo di solito accade quando c'è troppo accoppiamento nel codebase o sta cambiando troppo velocemente.

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

Generalmente, l'idea è quella 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)


Questo è un buon approccio. Sto cercando di ricordare cosa ha spinto davvero questa domanda ora ... Non penso che fosse qualcosa di molto suscettibile alla parallelizzazione. O se lo fosse, penso che la mia preoccupazione sia che questo approccio provochi molta frammentazione nella base di codice. Abbiamo "vecchi modi" di fare le cose e "nuovi modi" di fare le cose, e le vecchie cose vengono sostituite a un ritmo glaciale ma sta causando mal di testa agli sviluppatori perché ora devono essenzialmente conoscere due (o più) sistemi.
mpen

1

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

Dove lo sviluppo è orientato verso esigenze diverse in diversi settori è 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 Co-Dev "compromettono" una volta fatto dopo aver preso le giuste decisioni con gli input pertinenti di tutti gli sviluppatori, in caso di necessità le parole d'ordine ti permetteranno di implementare ciò di cui hai bisogno senza dover pensare: come unirò il mio codice.

ZBB è l'acronimo di Zero-based budgeting . Dicendo Co-Dev intendevo poche persone che lavorano nella programmazione parallela.


2
cosa sono "ZBB" e "Co-Dev"?
moscerino del

ZBB - en.wikipedia.org/wiki/Zero-based_budgeting . Dicendo Co-Dev intendevo poche persone che lavorano nella programmazione parallela.
Yosi Dahari,

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 hai poche possibilità di fare qualsiasi refactoring.


0

Il tuo problema è il modello di filiale che stai utilizzando. Potresti sviluppare su un ramo e quando completo e pronto per il controllo qualità, il ramo viene unito a un "tronco intermedio", a volte chiamato Integrazione o Test. Quando sviluppi la funzionalità successiva, puoi invece derivare da questo trunk intermedio.

Questo modello consente di sviluppare più funzionalità in parallelo su diversi rami, unendole tutte insieme nel ramo Integrazione per inviarle al QA e anche mantenere un unico trunk di rilasci (si unisce il QA codebase ricevuto al trunk principale quando lo certificano )

Stai supponendo che le tue modifiche inviate al QA verranno passate senza modifiche importanti - se il codice QA viene restituito con le istruzioni per rimuovere metà delle modifiche, dovrai ripristinare ma se ciò non accade, lo farà il tuo sviluppo è molto più fluido. Quindi stai sostanzialmente prendendo le filiali per nuove funzionalità da quello che sarà il tuo codice mainline (ovvero trunk dopo l'unione del codice passato al QA), piuttosto che quello che è oggi (ovvero trunk corrente) e quindi non si sviluppa più rispetto al codebase della versione precedente .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.