Mantiene centinaia di rami personalizzati sul ramo principale

139

Attualmente abbiamo un ramo principale per la nostra applicazione PHP in un repository condiviso. Abbiamo più di 500 clienti che sono abbonati al nostro software, la maggior parte dei quali ha una personalizzazione per scopi diversi, ciascuno in un ramo separato. La personalizzazione può essere un nome di campo di testo diverso, una funzionalità o un modulo completamente nuovi o nuove tabelle / colonne nel database.

La sfida che affrontiamo è che man mano che gestiamo queste centinaia di filiali personalizzate e le distribuiamo ai clienti, di tanto in tanto forniamo nuove funzionalità e aggiorniamo il nostro ramo principale, e vorremmo spingere le modifiche del ramo master ai rami personalizzati in per aggiornarli alla versione più recente.

Sfortunatamente questo spesso causa molti conflitti nel codice personalizzato, e passiamo molte ore ad attraversare ogni singolo ramo per risolvere tutti i conflitti. Questo è molto inefficiente e abbiamo scoperto che gli errori non sono rari quando si risolvono questi conflitti.

Sto cercando un modo più efficiente per mantenere aggiornati i rami di rilascio dei nostri clienti con il ramo principale, il che si tradurrà in un minore sforzo durante l'unione.

    
posta Fernando Tan 09.11.2015 - 17:00
fonte

9 risposte

308

Stai abusando completamente dei rami! Dovresti avere la personalizzazione basata sulla flessibilità della tua applicazione, non sulla flessibilità del controllo della versione (che, come hai scoperto, non è inteso / progettato per questo tipo di utilizzo).

Ad esempio, crea etichette di campi di testo provengono da un file di testo, non devono essere codificate nella tua applicazione (questo è il modo in cui funziona l'internazionalizzazione). Se alcuni clienti hanno caratteristiche diverse, rendi l'applicazione modulare , con rigidi confini interni governati da API stabili e stabili, in modo che le funzionalità possano essere inserite come necessario.

L'infrastruttura principale e tutte le funzionalità condivise devono solo essere archiviate, gestite e testate una volta .

Avresti dovuto farlo fin dall'inizio. Se hai già cinquecento varianti di prodotto (!), Risolverlo sarà un lavoro enorme ... ma non più della manutenzione in corso.

    
risposta data 09.11.2015 - 17:03
fonte
92

Avere 500 clienti è un bel problema, se avessi passato il tempo in anticipo per evitare questo problema con le filiali, potresti non essere mai stato in grado di continuare a negoziare abbastanza a lungo da ottenere clienti.

In primo luogo, spero che tu faccia pagare abbastanza ai tuoi clienti per coprire TUTTI i costi di mantenimento delle loro versioni personalizzate. Suppongo che i clienti si aspettino di ottenere nuove versioni senza dover pagare di nuovo le loro personalizzazioni. Vorrei iniziare trovando tutti i file uguali nel 95% dei tuoi rami. Quel 95% è la parte stabile della tua applicazione.

Quindi, trova tutti i file che hanno solo poche linee diverse tra i rami - prova a introdurre un sistema di configurazione tale che queste differenze possano essere rimosse. Quindi, ad esempio, anziché avere 100 file con etichette textfield diverse, hai 1 file di configurazione che può sostituire qualsiasi etichetta di testo. (Questo non deve essere fatto in un colpo solo, basta rendere un'etichetta del campo di testo configurabile la prima volta che un cliente vuole cambiarlo.)

Quindi passa ai problemi più difficili usando il modello di strategia, l'iniezione di dipendenza ecc.

Considera la possibilità di archiviare json nel database piuttosto che aggiungere colonne per i campi del cliente - questo potrebbe funzionare per te se non hai bisogno di cercare questi campi con SQL.

Ogni volta che controlli un file in un ramo, DEVI differirlo con main e giustificare ogni singola modifica, incluso lo spazio bianco. Molte modifiche non saranno necessarie e potranno essere rimosse prima del checkin. Questo potrebbe essere dovuto a uno sviluppatore che ha diverse impostazioni nel proprio editor per come il codice è formattato.

Si punta innanzitutto a passare da 500 filiali con molti file diversi, alla maggior parte dei rami solo alcuni file diversi. Mentre guadagni ancora abbastanza per vivere.

Potresti avere ancora 500 filiali tra molti anni, ma se sono molto più facili da gestire, allora hai vinto.

In base al commento di br3w5:

  • Si potrebbe prendere ogni classe che è diversa tra i clienti
  • Crea una "xxx_baseclass" che definisca tutti i metodi che vengono chiamati nella classe al di fuori di essa
  • Rinominare la classe in modo che xxx sia chiamato xxx_clientName (come sottoclasse di xxx_baseclass)
  • Utilizzare l'iniezione delle dipendenze in modo che venga utilizzata la versione corretta della classe per ciascun client
  • E ora per l'intelligente intuizione Br3w5 ha inventato! Utilizzare uno strumento di analisi del codice statico per trovare il codice ora duplicato e spostarlo nella classe base ecc

Esegui la procedura sopra descritta solo dopo aver ottenuto la grana facile, e seguila prima con alcune classi.

    
risposta data 09.11.2015 - 19:56
fonte
40

In futuro, chiedi al questionario il test Joel . Saresti più propenso a non fare un naufragio.

Questo è un, ah, come dovremmo dire ... davvero, davvero un brutto problema da avere. Il "tasso di interesse" su questo debito tecnico sarà molto, molto alto. Potrebbe non essere recuperabile ...

Quanto integrati con il "nucleo" sono queste modifiche personalizzate? Puoi creare loro una propria biblioteca e avere un singolo "core" e ogni cliente specifico ha il proprio "componente aggiuntivo?"

O queste sono tutte configurazioni molto minori?

Penso che la soluzione sia una combinazione di:

  • Modifica di tutte le modifiche hardcoded in elementi basati sulla configurazione. In questo caso ognuno ha la stessa applicazione di base, ma gli utenti (o tu) attivano / disattivano la funzionalità, impostano i nomi, ecc. Secondo necessità
  • Spostare funzionalità / moduli "specifici del cliente" per separare i progetti, quindi invece di avere un "progetto" hai un "progetto principale" con moduli che puoi aggiungere / rimuovere facilmente. In alternativa puoi anche fare queste opzioni di configurazione.

Né sarà banale come se tu fossi finito qui con più di 500 clienti, probabilmente non hai fatto alcuna distinzione in questo. Mi aspetto che i tuoi cambiamenti nella separazione di questa attività saranno molto dispendiosi in termini di tempo.

Ho anche il sospetto che tu abbia problemi significativi nel separare e classificare facilmente tutto il codice specifico del tuo cliente.

Se la maggior parte dei tuoi cambiamenti sono specificamente differenze di parole, ti suggerisco di leggere domande come questo sulla localizzazione della lingua. Sia che tu stia facendo più lingue interamente o solo un sottoinsieme, la soluzione è la stessa. Questo è specificamente PHP e localizzazione.

    
risposta data 09.11.2015 - 17:13
fonte
17

Questo è uno dei peggiori anti-pattern che puoi colpire con qualsiasi VCS.

L'approccio corretto qui è quello di trasformare il codice personalizzato in qualcosa guidato dalla configurazione, e quindi ogni cliente può avere la propria configurazione, sia codificata in un file di configurazione, o in un database o in qualche altra posizione. Puoi abilitare o disabilitare intere funzionalità, personalizzare la modalità di visualizzazione delle risposte e così via.

Ciò ti consente di mantenere un ramo principale con il tuo codice di produzione.

    
risposta data 09.11.2015 - 17:03
fonte
13

Lo scopo delle filiali è esplorare una possibile via di sviluppo senza rischiare di rompere la stabilità del ramo principale. Dovrebbero alla fine essere riuniti in un momento opportuno o essere scartati se conducono a un vicolo cieco. Quello che hai non sono così tante ramificazioni, ma ben 500 forcelle dello stesso progetto e provare ad applicare i changeset vitali a tutti loro è un compito siffoide.

Invece dovresti fare in modo che il tuo codice base risieda nel proprio repository, con i punti di accesso necessari per modificare il comportamento attraverso la configurazione e per iniettare il comportamento come consentito da dipendenze invertite .

Le diverse impostazioni che hai per i client possono quindi semplicemente distinguersi da uno stato configurato esternamente (ad esempio un database) o, se necessario, vivere come repository separati, che aggiungono il core come sottomodulo.

    
risposta data 09.11.2015 - 18:08
fonte
7

Tutte le cose importanti sono state proposte da buone risposte qui. Mi piacerebbe aggiungere i miei cinque pence come suggerimento di processo.

Vorrei suggerire di risolvere questo problema in un intervallo di lungo o medio termine e adottare la tua politica, come sviluppare il codice. Cerca di diventare un team di apprendimento flessibile. Se a qualcuno è consentito avere 500 repository invece di configurare il software, allora è il momento di chiedersi come hai lavorato fino ad ora e lo farai da ora in poi.

Che significa:

  1. Chiarire le responsabilità di change management: se un cliente ha bisogno di adattamenti, chi li sta vendendo, chi sta permettendo a loro e chi decide come il codice sarà cambiato? Dove sono le viti da girare se alcune cose devono essere cambiate?
  2. Chiarifica il ruolo, chi nel tuo team è autorizzato a creare nuovi repository e chi non lo è.
  3. Cerca di assicurarti che tutti nel tuo team vedano la necessità di modelli che consentano flessibilità al software.
  4. Chiarisci il tuo strumento di gestione: come fai a sapere rapidamente che cosa ha il cliente e quali adozioni del codice. Lo so, alcuni "elenchi di 500" suonano fastidiosi, ma qui c'è un po 'di "economia emotiva", se vuoi. Se non riesci a raccontare le modifiche del cliente in tempi rapidi, ti senti ancora più perso e disegnato come se dovessi iniziare una lista. Quindi, usa quell'elenco per raggruppare le funzioni come le altre risposte delle persone qui ti hanno mostrato:
    • raggruppa i clienti in base a modifiche minori / principali
    • raggruppa per modifiche correlate all'oggetto
    • raggruppa per modifiche facili da unire e cambia difficile da unire
    • trova gruppi di modifiche uguali apportate a diversi repository (oh sì, ce ne saranno alcuni).
    • forse il più importante per parlare con il tuo manager / investitore: raggruppa per costose modifiche e economici modifiche.

Questo non significa in alcun modo creare un'atmosfera di cattiva pressione nella tua squadra. Suggerisco piuttosto di chiarire prima questi punti per te stesso e, ovunque tu senta il sostegno, organizzalo insieme al tuo team. Invita le persone al tavolo per migliorare la tua esperienza.

Quindi, prova a stabilire una finestra temporale a lungo termine, in cui cucini questa cosa su una piccola fiamma. Suggerimento: cerca di unire almeno due repository ogni settimana, quindi rimuovere almeno uno . Potresti imparare che spesso puoi unire più di due rami, man mano che ricevi routine e supervisione. In questo modo, in un anno puoi gestire le peggiori (più costose?) Filiali e in due anni puoi ridurre questo problema per avere un software decisamente migliore. Ma non aspettarti di più, perché alla fine nessuno "avrà tempo" per questo, ma tu sei quello che non lo permetterà più come tu sei l'architetto del software.

Questo è il modo in cui proverei a gestirlo se fossi nella tua posizione. Tuttavia, non so come la tua squadra accetterà tali cose, come il software lo ammetta davvero, come sei supportato e anche cosa devi ancora imparare. Tu sei l'architetto del software, fallo e basta: -)

    
risposta data 10.11.2015 - 07:11
fonte
5

In contrasto con tutti i mendicanti, assumiamo le reali esigenze di business.

(ad esempio, il deliverable è il codice sorgente, i clienti provengono dalla stessa linea di business e quindi i concorrenti l'uno dall'altro, e il modello di business promette di mantenere segreti i propri segreti)

Inoltre, supponiamo che la tua azienda abbia gli strumenti per mantenere tutti i rami, ovvero sia manodopera (diciamo 100 sviluppatori dedicata alla fusione, supponendo un ritardo di rilascio di 5 giorni o 10 sviluppatori che assumono Intervallo di rilascio di 50 giorni è OK), o test automatici così straordinari che le unioni automatizzate sono veramente testate sia per specifiche core che per specifiche estensione in ogni ramo, e quindi solo le modifiche che non si fondono in modo "pulito" richiedono l'intervento umano. Se i tuoi clienti pagano non solo per le personalizzazioni ma per la loro manutenzione, questo potrebbe essere un modello di business valido.

La mia domanda (e nay-sayers) è, hai una persona dedicata responsabile della consegna a ciascun cliente? Se sei, per esempio, un'azienda di 10.000 persone, potrebbe essere il caso.

Questo potrebbe essere gestito da architettura dei plugin in alcuni casi, diciamo che il tuo core è trunk, i plugin possono essere tenuti in trunk o rami e la configurazione per ogni cliente è un file con un nome univoco o è tenuto nella filiale del cliente.

I plugin possono essere caricati in fase di esecuzione o integrati in fase di compilazione.

Molti progetti sono fatti in questo modo, fondamentalmente lo stesso problema si applica ancora: semplici cambiamenti di base sono banali da integrare, i cambiamenti di conflitto devono essere risolti o le modifiche sono necessarie per molti plugin.

Ci sono casi in cui i plug-in non sono abbastanza buoni, questo è il momento in cui molti interni del core devono essere ottimizzati affinché il numero di interfacce del plugin diventi troppo grande da gestire.

Idealmente questo sarebbe gestito da programmazione orientata all'aspetto , dove trunk è il codice principale, e le ramificazioni sono aspetti (cioè codice extra e istruzioni su come connettere gli extra al core)

Un semplice esempio, puoi specificare che foo personalizzato viene eseguito prima o dopo core klass.foo o che lo sostituisce, oppure che lo avvolge e può cambiare input o output.

Ci sono un sacco di librerie per questo, tuttavia il problema dei conflitti di unione non scompare: le unzioni pulite vengono gestite da AOP e i conflitti richiedono ancora l'intervento umano.

Finalmente questo tipo di attività deve preoccuparsi della manutenzione delle filiali , ovvero della caratteristica X specifica del cliente, così comune che è più economico spostarla verso il core, anche se non tutti i clienti lo pagano ?

    
risposta data 10.11.2015 - 16:50
fonte
3

Non stai risolvendo la causa alla radice della malattia osservando il sintomo. L'utilizzo di un approccio di "gestione del codice" è sintomatico, ma non risolverà le cose a lungo termine. La causa principale è la mancanza di funzionalità, caratteristiche e amp; le loro estensioni e variazioni.

Il tuo codice "personalizzato" non rappresenta altro che estensioni delle caratteristiche del prodotto & capacità e campo dati cambia ad altri.

Quanto sono estese le funzionalità personalizzate, quanto differenti, quanto contestualmente simili o meno giocheranno molto nel "sanitizzare" la base di codice del tuo prodotto.

Più che come codice e versione, questo è un luogo in cui entra in gioco la gestione del prodotto, l'architettura del prodotto e l'architettura dei dati . Scherzi a parte.

Perché, alla fine della giornata, il codice non è altro che la tua offerta di attività e servizi / servizi del prodotto ai tuoi clienti. Questo è ciò per cui la tua azienda viene pagata.

Affrontare meglio questo deve venire dal punto di vista delle "capacità" e non dal punto di vista del codice.

Tu, la tua azienda e il tuo prodotto non possono essere tutto per tutti. Ora che hai una base di entrate decente di 500 clienti, è il momento di passare a quello che intendi essere.

E se offri parecchie cose, avrebbe senso modulare le tue capacità di prodotto in modo organizzato.

Quanto saranno ampi e profondi i tuoi prodotti? Oppure questo porterà a problemi di "qualità del servizio" & "diluizione e frammentazione del prodotto" man mano che procedi.

Sarai CRM o ERP o elaborazione degli ordini / spedizione o Microsoft Excel?

Le tue estensioni esistenti devono essere arrotolate e armonizzate, come un grande software estrae e unisce prodotti acquisiti da un avvio.

Dovrai disporre di una strong gestione del prodotto e dell'architettura dei dati come ad esempio la mappa:

  • Filiale principale, le sue funzionalità di prodotto e le funzionalità di base
  • Funzioni di estensione personalizzate, tipi e varianti
  • Significato e variazione dei "campi personalizzati"

.. per creare una road map di assimilazione e armonizzazione di tutti questi thread / rami di prodotto liberi nel grande contesto dell'applicazione principale.

PS: Connettiti con me, conosco una persona che può aiutarti a risolvere questo problema:)

    
risposta data 10.11.2015 - 06:21
fonte
-5

Posso identificarmi con questo. Ho preso molti progetti su. In effetti, il 90% del nostro lavoro di sviluppo sta sistemando queste cose. Non tutti sono perfetti, quindi ti suggerisco di usare il controllo della versione nel modo corretto e dove sei, se possibile puoi fare quanto segue.

  • D'ora in poi, quando un cliente richiede un aggiornamento, spostali nel nuovo repository a forcella.
  • Se vuoi unirli per padroneggiarlo, fallo come prima cosa e risolvi i conflitti.
  • Quindi gestisci i loro problemi e sprint con il loro repository e mantieni quelli master che vuoi avviare in master. Ciò potrebbe mettere a dura prova i cicli di rilascio, ma ciò ti farà risparmiare tempo.
  • Mantenere un ramo principale del repository principale per i nuovi clienti e il repository principale dovrebbe avere solo i rami su cui si sta lavorando per le cose future. Le filiali legacy possono essere eliminate una volta migrate nei repository dei clienti.

Ho importato personalmente un repository da GitHub con 40 filiali a Bitbucket e creato 40 repository. Ci sono volute solo quattro ore. Si trattava di variazioni del tema WordPress in modo che il push and pull fosse rapido.

Ci sono molte ragioni per cui "non lo faccio bene la prima volta", e penso che coloro che li accettano rapidamente e passino a "fare bene questa volta" avrebbero sempre successo.

    
risposta data 09.11.2015 - 22:01
fonte

Leggi altre domande sui tag