Come controlliamo quali funzionalità vengono rilasciate?

6

Sto lavorando in un ambiente in cui regolarmente iniziamo a lavorare su una funzione e poi ci viene chiesto di non rilasciarlo. Creiamo quindi altre funzionalità che vogliamo rilasciare.

Il mio tipico flusso di lavoro è la funzione di scrittura - > commit to default.

Questo flusso di lavoro ha funzionato bene storicamente, ma in questo ambiente si interrompe perché è necessario rimuovere le funzionalità dal ramo.

La mia idea di risolvere questo problema sarebbe quella di avere un ramo dev, in cui copiamo specifici commit relativi a una determinata funzione.

Usiamo sia git che mercurial. Il nostro tracker dei problemi è ALM (che è incidentalmente il peggior software con cui abbia mai lavorato).

Esiste un modo "standard" per gestire questo tipo di sviluppo? Esistono strumenti che supportano questo flusso di lavoro? Questi sistemi VCS hanno alcuni mezzi meno noti per supportarlo?

    
posta canisrufus 15.07.2015 - 16:08
fonte

3 risposte

4

Ogni ramo ha un ruolo. La ramificazione di una funzione è buona poiché consente al ramo della caratteristica di identificare in modo specifico tale ruolo.

L'unione sempre con i valori predefiniti implica che il default sia il ruolo di mainline, accumulation e packaging - ed è qui che si verificano problemi.

Dovresti invece prendere in considerazione l'idea di creare un ramo per il ruolo di "accumulo per il rilascio" e quindi unire le tue caratteristiche nel ramo di accumulo appropriato.

C'è niente che dice che non puoi avere più rami di accumulo simultanei. Un ramo per "next target release" che ha anche il nome "2.0" (più nomi su un ramo possono rendere più semplice la comprensione dell'intento di ciascuno - sebbene possa anche rendere più complicato fare tutti gli aggiornamenti dei nomi) e poi un altro ramo per '3.0'.

Se la tua funzione non è destinata a essere in 2.0, ma è destinato a essere in 3.0, uniscilo in 3.0.

Una volta completato con il ramo di rilascio 2.0 / successivo, unire quello di nuovo in predefinito e rilasciarlo. Quindi si dirama nuovamente per il successivo ramo di rilascio da predefinito e si uniscono 3.0 nel successivo ramo di rilascio e si continua da lì.

Questo ha il vantaggio di poter costruire in modo pulito il ramo 3.0 in qualsiasi momento per vedere se ci sono problemi all'orizzonte - prima che il 2.0 venga rilasciato. Separa anche i ruoli e costruisce le politiche per ogni ramo consentendo loro di essere comprensibili.

Ulteriori letture (una delle mie preferite per quanto riguarda la ramificazione e il controllo del codice sorgente): Strategie di ramificazione avanzata di SCM . In particolare, spiega che cos'è ciascun ruolo e come lavorano insieme. Sebbene il suo focus sia per forza, può essere applicato in modo pulito a git e ad altri sistemi di controllo delle versioni distribuite. Una volta letto, dai un'occhiata a git flow e osserva come questi concetti vengono applicati modello.

    
risposta data 15.07.2015 - 16:41
fonte
3

Penso che il tuo flusso di lavoro di scrivere una funzionalità (o una correzione di bug) e poi di commetterlo al "default" (che sembra essere un ramo principale o di linea) sia il problema. Invece, considera l'utilizzo di una strategia di ramificazione alternativa.

In genere mi concentro solo su una versione alla volta, quindi c'è un ramo "di integrazione" fuori dal trunk e poi ci sono rami di sviluppo per correzioni di bug o funzionalità (e talvolta sono raggruppati se ha senso raggrupparli e forzare l'accettazione di uno per guidare l'accettazione di altre correzioni di bug o funzionalità). Quando una funzionalità è accettata per il rilascio, la spostiamo da un ramo di sviluppo a un ramo di integrazione, dove farà parte di una build notturna e passerà attraverso un ciclo di test. Quando rilasciamo, passiamo dal ramo di integrazione al trunk.

Esistono anche altre strategie di ramificazione . Se cerchi una combinazione della frase "strategia di ramificazione" e del tuo sistema di controllo delle versioni, probabilmente troverai molti articoli e post di blog che potrebbero essere più pertinenti.

    
risposta data 15.07.2015 - 16:37
fonte
2

Un modo (comune) di risolvere il tuo problema è con l'uso di rami, come descritto dalle altre risposte. Tuttavia, c'è un'altra soluzione: attiva / disattiva (ovvero flag di funzionalità).

L'idea di base è che il tuo programma mantenga una lista di flag (i flag delle caratteristiche) che possono essere attivati e disattivati, e il codice che implementa la funzionalità controlla il flag della funzione corrispondente prima di fare il suo lavoro. Quindi avresti opzioni di attivazione delle funzioni come "ENABLE_NEW_LOGIN_FORM", "USE_NEW_TAX_ALGORITHM", "SHOW_TEASER_FOR_NEW_USER". Ci sono molti modi per implementarlo e molte librerie lo fanno per te, ma questa è l'idea di base.

Vantaggi (rispetto all'utilizzo di rami):

  • Puoi unire a main / master non appena viene eseguita una funzione; questo evita di mantenere i rami per molto tempo, il che può significare molto lavoro (cioè unire conflitti) se per esempio ci sono dei refactoring nel ramo principale.
  • Hai un elenco di tutte le funzioni che sono state completate (l'elenco delle opzioni di attivazione / disattivazione), invece di molte sezioni delle funzioni, le cui funzionalità potrebbero essere completate o meno.
  • Puoi posticipare la decisione se rilasciare una funzionalità o meno e cambiare idea in qualsiasi momento (come fanno le parti interessate). Al contrario, una volta che un ramo viene unito, la rimozione del codice è complicata.
  • È possibile attivare le funzionalità dopo l'installazione o anche in fase di esecuzione, se lo si desidera (anche se questo può aggiungere un po 'di complessità).

Svantaggi

  • Hai bisogno di un po 'di codice e di un'infrastruttura extra per mantenere i commutatori di funzionalità.
  • Avrai bisogno di alcuni brutti (?) if/else di controlli nel codice di ogni funzione che può essere attivata (sebbene alcuni linguaggi / framework abbiano delle belle alternative).
  • Devi testare con entrambe le funzioni spente e accese, quindi più test.
  • A volte, dovrai mantenere due codepath in parallelo per i due stati di attivazione, invece di essere in grado di rimuovere il vecchio codice.
  • Alla fine devi eliminare gli interruttori di funzione per evitare che si accumulino, il che significa un po 'di lavoro extra.

Ho usato le levette di funzionalità quando i rami di mantenimento sono diventati troppo dolorosi a causa di conflitti di merge e li ho trovati un'ottima soluzione, per i motivi illustrati sopra.

L'implementazione di solito non è difficile; aiuta ad avere una buona funzionalità che alterna la libreria che funziona bene con il linguaggio / framework / runtime che si usa e ha le caratteristiche che ti servono (pensa a cose come la memorizzazione delle levette delle funzioni, l'amministrazione semplice, il supporto per il passaggio al runtime ...) . Abbiamo usato Togglz (per Java), ma ci sono molte opzioni.

Vedi anche: Feature Toggles vs Feature Branches

    
risposta data 15.07.2015 - 22:01
fonte

Leggi altre domande sui tag