La migliore strategia di ramificazione quando si esegue l'integrazione continua?


100

Qual è la migliore strategia di ramificazione da utilizzare quando si desidera eseguire l'integrazione continua?

  1. Ramo di rilascio : sviluppa sul tronco, mantieni un ramo per ogni rilascio.
  2. Feature Branching: sviluppa ogni funzionalità in un ramo separato, unisci solo una volta stabile.

Ha senso utilizzare entrambe queste strategie insieme? Come in, si ramifica per ogni versione ma si ramifica anche per grandi funzionalità? Una di queste strategie si integra meglio con l'integrazione continua? L'uso dell'integrazione continua avrebbe senso anche quando si utilizza un trunk instabile?


2
Nota a margine: alcuni sostengono che anche se vengono inserite nuove funzionalità, tutto dovrebbe essere sempre stabile. D'altra parte, potrebbe essere un po 'idealistico.
Keith Pinson

Risposte:


21

Trovo l'argomento davvero interessante poiché faccio molto affidamento sulle filiali nel mio lavoro quotidiano.

  • Ricordo Mark Shuttleworth che proponeva un modello per mantenere intatto il ramo principale andando oltre la CI convenzionale. L'ho postato qui .
  • Dato che ho familiarità con Cruise Control, ho anche scritto sul blog di rami di attività e CI qui . È un tutorial passo passo che spiega come farlo con Plastic SCM .
  • Infine, ho trovato molto interessanti anche alcuni degli argomenti su CI (e potenzialmente parlare di branching) nel libro di Duvall su CI .

Spero che tu trovi i link interessanti.


Abbiamo aggiunto il supporto a Bamboo per eseguire branch per task codicesoftware.blogspot.com/2012/02/… , e sembra che la loro versione più recente lo farà in modo nativo con diversi controlli di versione, incluso dvcs.
pablo

20

La risposta dipende dalle dimensioni del team, dalla qualità del controllo del codice sorgente e dalla capacità di unire correttamente serie di modifiche complesse. Ad esempio nel controllo del codice sorgente completo come CVS o SVN l'unione può essere difficile e potresti stare meglio con il primo modello, mentre se utilizzi un sistema più complesso come IBM ClearCase e con un team di dimensioni maggiori potresti essere migliore con il secondo modello o una combinazione dei due.

Personalmente separerei il modello del ramo delle caratteristiche, in cui ogni caratteristica principale è sviluppata su un ramo separato, con rami secondari delle attività per ogni modifica effettuata dal singolo sviluppatore. Man mano che le funzionalità si stabilizzano, vengono unite al trunk, che viene mantenuto ragionevolmente stabile e supera tutti i test di regressione in ogni momento. Man mano che ti avvicini alla fine del tuo ciclo di rilascio e tutti i rami delle funzionalità si fondono, stabilisci e ramifica un ramo del sistema di rilascio su cui esegui solo correzioni di bug di stabilità e backport necessari, mentre il tronco viene utilizzato per lo sviluppo della versione successiva e tu di nuovo diramazione per nuovi rami di funzionalità. E così via.

In questo modo il trunk contiene sempre il codice più recente, ma riesci a mantenerlo ragionevolmente stabile, creando etichette stabili (tag) su modifiche importanti e fusioni di funzionalità, i rami delle funzionalità sono uno sviluppo veloce con integrazione continua e i singoli rami secondari delle attività possono essere spesso aggiornato dal ramo delle funzionalità per mantenere sincronizzati tutti coloro che lavorano sulla stessa funzionalità, senza influire contemporaneamente su altri team che lavorano su funzionalità diverse.

Allo stesso tempo hai nella cronologia una serie di rami di rilascio, dove puoi fornire backport, supporto e correzioni di bug per i tuoi clienti che per qualsiasi motivo rimangono sulle versioni precedenti del tuo prodotto o anche solo sull'ultima versione rilasciata. Come per il trunk, non si imposta l'integrazione continua sui rami di rilascio, questi vengono attentamente integrati dopo aver superato tutti i test di regressione e altri controlli di qualità del rilascio.

Se per qualche motivo due funzionalità sono co-dipendenti e necessitano di modifiche reciproche, si può considerare di svilupparle entrambe sullo stesso ramo di funzionalità o di richiedere alle funzionalità di unire regolarmente parti stabili del codice al trunk e quindi aggiornare le modifiche da trunk per scambiare il codice tra i rami del trunk. Oppure, se è necessario isolare queste due funzionalità dalle altre, è possibile creare una diramazione comune da cui ramificare tali diramazioni e che è possibile utilizzare per scambiare codice tra le funzionalità.

Il modello sopra non ha molto senso con team con meno di 50 sviluppatori e un sistema di controllo del codice sorgente senza rami sparsi e capacità di fusione adeguate come CVS o SVN, il che renderebbe l'intero modello un incubo da configurare, gestire e integrare.


5
Non sono sicuro di essere d'accordo sul fatto che ciò che descrivi non abbia senso per i team con meno di 50 sviluppatori. Vedo vantaggi anche per team molto più piccoli. +1
Aardvark

2
Naturalmente, ci sono vantaggi per i team di qualsiasi dimensione. La domanda è a quale dimensione del team i benefici superano i costi associati a un processo pesante.
Jiri Klouda

È simile al modello GitFlow e / o GitHubFlow. Non credo che questi modelli facilitino l'integrazione continua (CI). A mio parere, lo sviluppo basato sul tronco è un miglioramento significativo su questi modelli.
Yani

Puoi vedere che questo commento in realtà è precedente alla versione originale di git flow. Non sono sicuro di cosa intendi per "migliore". Ho supportato team di 1, 5, 25, 150, 1.000 e 20.000 sviluppatori che lavorano su progetti che sono stati integrati in una certa misura. I requisiti variano e "migliore" è un termine molto relativo. Hai mai bisogno di backport del codice? Correzioni di sicurezza? In caso contrario, la tua vita è semplice. SaaS è il risultato diretto delle restrizioni imposte dallo sviluppo basato su trunk. I flag di funzionalità sono complessi quanto i rami di funzionalità. Tranne che lo scopri dai clienti solo quando una loro permutazione si interrompe.
Jiri Klouda

9

Personalmente trovo molto più pulito avere un tronco stabile e ramificare le funzionalità. In questo modo, i tester e simili possono rimanere su una singola "versione" e aggiornare dal trunk per testare qualsiasi funzionalità che sia completa di codice.

Inoltre, se più sviluppatori stanno lavorando su funzionalità diverse, possono avere tutti i propri rami separati, quindi unirsi al trunk quando hanno finito e inviare una funzionalità da testare senza che il tester debba passare a più rami per testare funzionalità diverse.

Come bonus aggiuntivo, c'è un certo livello di test di integrazione che viene fornito automaticamente.


Inoltre, continui a ramificare e taggare per ogni versione principale? O semplicemente tag?
KingNestor

1
Funziona bene con CI fintanto che i rami delle funzionalità vengono uniti in trunk con una certa disciplina in modo da non avere build interrotte. Eseguo branch e tag per ogni versione di produzione che verrà utilizzata solo per la risoluzione dei bug. Questo può essere unito immediatamente al tronco stabile.
Adnan

@king Direi che probabilmente dipende da ciò che chiami major release, ma in entrambi i casi puoi taggare e ramificare più tardi quando ne hai bisogno (in base al tag :))
eglasius

5

Penso che entrambe le strategie possano essere utilizzate con lo sviluppo continuo, a condizione di ricordare uno dei principi chiave che ogni sviluppatore si impegna ogni giorno per trunk / mainline.

http://martinfowler.com/articles/continuousIntegration.html#EveryoneCommitsToTheMainlineEveryDay

MODIFICARE

Ho letto questo libro su CI e gli autori suggeriscono che la ramificazione per rilascio è la loro strategia di ramificazione preferita. Sono d'accordo. La ramificazione per funzionalità non ha senso per me quando si utilizza CI.

Proverò a spiegare perché penso in questo modo. Supponiamo che tre sviluppatori scelgano ciascuno un ramo per lavorare su una funzionalità. Ciascuna funzionalità richiederà diversi giorni o settimane per essere completata. Per garantire che il team si integri continuamente, devono impegnarsi nel ramo principale almeno una volta al giorno. Non appena iniziano a farlo, perdono il vantaggio di creare un ramo di funzionalità. Le loro modifiche non sono più separate da tutte le modifiche degli altri sviluppatori. Stando così le cose, perché preoccuparsi di creare rami di funzionalità in primo luogo?

L'uso della ramificazione per rilascio richiede molto meno fusione tra rami (sempre una buona cosa), assicura che tutte le modifiche vengano integrate al più presto e (se fatto correttamente) assicura che la tua base di codice sia sempre pronta per il rilascio. Lo svantaggio del branching per release è che devi stare molto più attento alle modifiche. Ad esempio, il refactoring di grandi dimensioni deve essere eseguito in modo incrementale e se hai già integrato una nuova funzionalità che non desideri nella prossima versione, deve essere nascosta utilizzando un qualche tipo di meccanismo di commutazione delle funzionalità .

UN'ALTRA MODIFICA

C'è più di un'opinione su questo argomento. Ecco un post sul blog che è una funzionalità pro ramificata con CI

http://jamesmckay.net/2011/07/why-does-martin-fowler-not-understand-feature-branches/


interessante, non riesco più a trovare questo post.
Jirong Hu

5

I rami di rilascio sono molto utili e persino assolutamente necessari se è necessario mantenere diverse versioni della tua app.

Anche i feature branch sono molto convenienti, in particolare se uno sviluppatore deve lavorare su un cambiamento enorme, mentre altri rilasciano ancora nuove versioni.

Quindi per me usare entrambi i meccanismi è un'ottima strategia.

Link interessante dal Libro di SVN .


4

Recentemente ho apprezzato questo modello quando utilizzo git. Sebbene la tua domanda sia contrassegnata come "svn", potresti comunque essere in grado di utilizzarla.

L'integrazione continua può avvenire in una certa misura nel ramo "sviluppo" (o come lo si chiama) in questo modello, sebbene avere rami di funzionalità di lunga durata per le versioni future non lo renderebbe così rigido da considerare ogni cambiamento che si verifica nel codice da qualche parte. La domanda rimane, se lo vuoi davvero. Martin Fowler lo fa.


2

L'integrazione continua non dovrebbe essere un fattore di alcun tipo nel determinare la strategia di ramificazione. Il tuo approccio di ramificazione dovrebbe essere selezionato in base al tuo team, al sistema in fase di sviluppo e agli strumenti a tua disposizione.

Avendolo detto ...

  • non c'è motivo per cui CI non possa essere utilizzato in entrambi gli approcci che descrivi
  • questi approcci funzionano abbastanza bene in combinazione
  • nessuno dei due funziona "meglio" dell'altro
  • CI ha perfettamente senso con un tronco instabile

A tutto questo è stata data risposta nella quarta domanda sulla pagina da cui hai preso i diagrammi: http://blogs.collab.net/subversion/2007/11/branching-strat/


2

Finché comprendi i principi, puoi sempre reinventare le migliori pratiche. Se non capisci i principi, le migliori pratiche ti porteranno lontano prima di cadere a pezzi a causa di alcuni requisiti esterni in conflitto.

Per una migliore introduzione al modello Mainline, leggi questo: https://web.archive.org/web/20120304070315/http://oreilly.com/catalog/practicalperforce/chapter/ch07.pdf

Leggi il link. Una volta acquisite le basi, leggi il seguente articolo del venerabile Henrik Kniberg. Ti aiuterà a mettere in relazione il modello Mainline con l'integrazione continua.

http://www.infoq.com/articles/agile-version-control


Il capitolo O'Reilly non è più accessibile
Jason S

1

Quando abbiamo creato il nostro team, abbiamo ereditato una strategia basata sul rilascio dal fornitore che originariamente aveva sviluppato il sistema di cui stavamo per incaricarci. Ha funzionato fino al momento in cui i nostri clienti hanno richiesto che diverse funzionalità sviluppate non dovessero essere incluse in una versione (fyi ~ 250k righe di codice, ~ 2500 file, Scrum con XP SDLC).

Quindi abbiamo iniziato a esaminare i rami basati sulle funzionalità. Anche questo ha funzionato per un po ', come 2 mesi fino al punto in cui ci siamo resi conto che il nostro processo di test di regressione avrebbe richiesto più di 2 settimane, il che, combinato con l'incertezza di ciò che sarebbe stato rilasciato, ha creato un enorme inconveniente.

Il "chiodo nella bara" finale delle strategie SC pure è arrivato quando abbiamo deciso che avremmo dovuto avere 1. tronco stabile e 2. La produzione dovrebbe contenere ST, UAT e BINARI testati per la regressione (non solo la fonte - pensa CC.)

Questo ci ha portato a ideare una strategia che è un ibrido tra strategie SC basate su funzionalità e rilascio.

Quindi abbiamo un baule. Ogni sprint diramiamo il ramo dello sprint (per le persone non agili - uno sprint è solo uno sforzo di sviluppo time-boxed con output variabile basato sulla complessità). Dal ramo dello sprint creiamo i rami delle funzionalità e lo sviluppo parallelo inizia in essi. Una volta che le funzionalità sono complete e il sistema testato, e riceviamo l'intenzione di distribuirle, vengono unite al ramo sprint: alcune possono fluttuare su diversi sprint, di solito i più complessi. Una volta che lo sprint è vicino alla fine e le funzionalità sono complete ... "rinominiamo" il ramo dello sprint in "regressione" (questo consente a CruiseControl di riprenderlo senza alcuna riconfigurazione) e quindi il test di regressione / integrazione inizia sul cc-built ORECCHIO. Quando tutto è finito, entra in produzione.

In breve, i rami basati sulle funzionalità vengono utilizzati per sviluppare, test di sistema e funzionalità UAT. Il ramo sprint (in realtà il ramo di rilascio) viene utilizzato per unire selettivamente funzionalità su richiesta e test di integrazione.

Ora ecco una domanda per la comunità: ovviamente abbiamo problemi a eseguire l'integrazione continua a causa del fatto che lo sviluppo avviene su molti rami e del sovraccarico di riconfigurazione di CruiseControl. Qualcuno può suggerire e consigliare?


Non sono necessariamente d'accordo con le conclusioni, ma grazie per la discussione del tuo processo. Non esiste una soluzione valida per tutti.
RaoulRubin

0

Per come la vedo io, vuoi avere un insieme limitato di rami su cui puoi concentrarti. Poiché desideri test, metriche sulla qualità del codice e molte cose interessanti da eseguire con le build, avere troppi rapporti probabilmente ti farà perdere informazioni.

Quando e cosa ramificare, di solito dipende dalle dimensioni del team e dalle dimensioni delle funzionalità sviluppate. Non credo che ci sia una regola d'oro. Assicurati di utilizzare una strategia in cui puoi ottenere feedback presto / spesso e ciò include il coinvolgimento della qualità sin dall'inizio delle funzionalità. La parte di qualità, significa che mentre automatizzi mentre il team si sviluppa, se dividi per un ampio set di funzionalità che un team sta costruendo, devi coinvolgere anche la qualità nel team.

ps Dove hai preso quei riferimenti di approccio? - non ritiene che quei grafici rappresentino tutte le opzioni

Aggiornamento 1: espandendo il motivo per cui ho detto che non è una regola d'oro. Fondamentalmente per team relativamente piccoli ho trovato il modo migliore di utilizzare un approccio che è un mix. I rami delle funzionalità vengono creati se è qualcosa di lungo e parte del team continuerà ad aggiungere funzionalità più piccole.


Ne ha anche dell'altro. Ma sento che Feature Branching e Release Branching sono i due più comuni.
KingNestor

0

Dave Farley , un autore di Continuous Delivery , ha definito Trunk Based Development (TBD) come la pietra angolare di Continuous Integration (CI) e Continuous Delivery (CD). Lui dice:

Qualsiasi forma di ramificazione è antitetica all'integrazione continua.

Dice anche,

Feature Branching è molto bello dal punto di vista di un singolo sviluppatore ma non ottimale dal punto di vista di un team. Vorremmo tutti poter ignorare quello che fanno gli altri e andare avanti con il nostro lavoro. Sfortunatamente, il codice non è così. Anche in basi di codice molto ben strutturate con una bella separazione delle preoccupazioni e componenti meravigliosamente liberamente accoppiati, alcune modifiche influenzano altre parti del sistema.

Trunk Based Development (TBD) è la pratica di integrare le modifiche al codice nel trunk (noto anche come master, mainline) almeno una volta al giorno, preferibilmente più volte al giorno. L'integrazione continua (CI) è una pratica simile tranne per il fatto che comporta anche la verifica delle modifiche al codice mediante test automatizzati. La migliore strategia di ramificazione per questo è lavorare direttamente dal trunk ed eseguire le revisioni del codice attraverso la programmazione in coppia . Se per qualche motivo non riesci ad accoppiarti o vuoi solo diramare, assicurati che i tuoi rami siano di breve durata (meno di un giorno).

Lavoro su Trunk, "master" nei miei repository GIT. Mi impegno a eseguire il master in locale e inviare immediatamente, quando sono in rete, al mio repository principale centrale in cui viene eseguito CI. Questo è tutto!

Per le funzionalità di grandi dimensioni (ovvero quelle che richiedono più di un giorno), provare a suddividerle in piccoli blocchi di logica che possono essere integrati nel trunk senza interrompere il software. È inoltre possibile utilizzare tecniche come il contrassegno delle funzionalità e la ramificazione per astrazione che consentono di distribuire il lavoro incompleto senza influire sugli utenti finali.

Uso branch by abstraction, dark-release e talvolta feature-flag. Quello che ottengo in cambio è un feedback rapido e definitivo (almeno per la qualità dei miei test).


Dave Farley e Jez Humble hanno semplicemente torto nella loro posizione sulla ramificazione. La ragione è che codifica un presupposto importante "non dovrai mai manipolare il codice a livello di funzionalità e se, allora va bene che sia un'operazione costosa" e basano la loro valutazione su un altro presupposto "l'unione è troppo costosa con l'automazione si fondono essendo quasi impossibili su larga scala ". Se queste due ipotesi non sono vere, se vivi in ​​un mondo in cui l'unione costa poco, ma devi manipolare il codice a livello di funzionalità per le porte posteriori e le correzioni di sicurezza, le loro dichiarazioni si interrompono. È un caso raro però.
Jiri Klouda

Alcune aziende devono anche spostare le funzionalità in avanti alle versioni future, dopo che tali funzionalità hanno incontrato ostacoli nell'implementazione e stanno ritardando una versione. A volte c'è un'opzione per lasciare il codice, come nei prodotti SaaS, ma se il codice viene rilasciato ai clienti, potrebbe non essere un'opzione in quanto può essere analizzato dai concorrenti. Al giorno d'oggi così tanto codice non viene compilato e anche se lo è, i flag di definizione / funzionalità nel codice sono allo stesso livello di complessità dei rami.
Jiri Klouda

-3

Penso che gli strumenti che usi siano un fattore importante qui.

  • Se stai usando subversion, mantieni l'opzione 1 e rilascia dai rami.
  • Se stai usando GIT, l'opzione 2 funzionerà bene per te.

2
La ramificazione delle caratteristiche può essere facilmente ottenuta con qualsiasi SCM
hdost
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.