Software Engineering 5 commenti

DVCS... Chi era costui?

di Valentino Volonghi

L’introduzione del paradigma distribuito nel campo dei VCS (Version Control System) porta a un’evoluzione ben più ampia del semplice processo di sviluppo individuale del software, il cambiamento è infatti radicale e riguarda l’intero gruppo di lavoro.

In questo articolo ci concentreremo sullo spiegare le situazioni in cui i sistemi distribuiti offrono i maggiori vantaggi rispetto alle soluzioni centralizzate ben più diffuse e note e che quindi non necessitano degli stessi chiarimenti. Cercheremo anche di separare le caratteristiche di livello implementativo, come particolari comandi o un’eventuale interfaccia grafica, da quelle dovute al cambio di workflow del nuovo sistema.

La storia dei sistemi centralizzati OpenSource inizia molti anni fa con RCS e passando per CVS arriva all’ultima incarnazione chiamata Subversion che può essere considerato il software di VCS più diffuso e utilizzato al mondo.

I sistemi centralizzati si caratterizzano per il fatto che esiste un solo repository centrale a cui tutti gli sviluppatori devono fare riferimento per lo sviluppo. Proprio in questo consiste la principale differenza con i sistemi distribuiti dove, sebbene possa esistere un unico repository a cui tutti fanno riferimento, ciascuno sviluppatore nel momento del checkout crea localmente una copia identica del repository principale e può quindi lavorare localmente senza bisogno di continua sincronizzazione oppure può scegliere di sincronizzarsi con repository diversi da quello principale.

Questa differenza ne introduce contemporaneamente un’altra riguardante l’atto pratico dell’utilizzo: poiché la copia locale del repository è identica a quella remota, il singolo passo di commit dei sistemi centralizzati viene diviso in commit e push, ovvero nella conferma dei cambiamenti registrando il changeset in locale e la copia dei nuovi changeset nel repository remoto di riferimento (o di nostra scelta, non siamo più vincolati al nostro repository principale).

Per procedere nell’analisi però diventa utile introdurre alcuni workflow che si differenziano per essere i più frequentemente presenti nelle nostre lunghe giornate di lavoro. Analizzeremo quindi il comportamento dei due concorrenti alla prova dei fatti.

Sviluppo Centralizzato

Sviluppo centralizzato

Figura 1. La figura rappresenta lo schema logico di funzionamento di un sistema centralizzato

Il primo, e più scontato, flusso è quello tipico dei sistemi centralizzati, si può dividere nei passi:

  1. Checkout del repository principale
  2. Modifica locale
  3. Commit dei cambiamenti
  4. Push dei changeset sull'albero principale (solo nei sistemi distribuiti)

Non esistono particolari problemi strutturali che possano impedire ai sistemi distribuiti di funzionare in questo modo e ovviamente i sistemi centralizzati sono stati creati per supportare principalmente questo ciclo di sviluppo.

Sviluppo Branch-Based

Sviluppo branch-based

Figura 2. In figura è rappresentato lo schema logico dello sviluppo branch based. Le frecce indicano la direzione dello scambio di dati ed è possibile notare che nei sistemi distribuiti questo può avvenire anche tra i computer degli sviluppatori e non solo tra l'albero principale e i branch.

Negli ultimi anni si sta affermando un sistema di sviluppo che si fonda sul funzionamento continuo del ramo principale del repository. Spesso negli ambienti agili lo sviluppo branch-based viene affiancato da una tecnica chiamata Continuous Integration. I benefici principali ottenibili riguardano appunto l'integrazione tra diverse componenti del software, che possono essere sviluppate indipendentemente da più gruppi diversi, che utilizzano le ultime versioni disponibili senza preoccuparsi di eventuali bachi nei componenti usati in quanto ciascun albero è garantito essere funzionante in ogni momento. Questo permette di ridurre drasticamente i tempi di integrazione mentre ci si avvicina al rilascio e riduce notevolmente il numero di bachi grazie alla continua osservazione dello stato funzionale dei componenti e del sistema nel suo complesso. È facile comprendere come l'uso di ciascuna di queste tecniche permette di aumentare la qualità del software che sviluppiamo e di ridurre i momenti di panico dovuti a bachi estremamente difficili da trovare e quindi correggere.

Per fornire queste possibilità il modello tende a creare nuovi rami di sviluppo, i cosidetti branch, per sviluppare nuove funzionalità o correggere bachi esistenti, e una volta terminato il lavoro si prosegue a riunire i due rami attraverso l’operazione di merging.

Nei sistemi centralizzati come Subversion si cerca di ottenere questa funzionalità strutturando l’albero in almeno 2 sottodirectory e usando il comando di copia come comando di branch. Come conseguenza di questo, si ottiene una fisionomia dei repository che può essere spesso ricondotta alle seguenti 3 directory:

trunk/
branches/
tags/

Nel momento in cui lo sviluppatore intende lavorare a una nuova funzionalità, crea una copia dell’ultima versione del repository, presente in trunk, ad esempio nella directory branches/new_cool_feature e prosegue quindi il suo lavoro in questo nuovo branch.

Nei sistemi distribuiti come Mercurial si procederebbe creando una copia dell’albero di riferimento, lavorando quindi su di essa e procedendo nel push dei changeset interessati verso l’albero di riferimento.

Vi sarete accorti della pochissima differenza presente tra il modello di sviluppo centralizzato e questo modello per quanto riguarda i sistemi distribuiti. In realtà non c’è nessun problema in questo poiché i moderni DVCS sono stati creati proprio pensando allo sviluppo branch-based rendendolo, di fatto, uguale allo sviluppo centralizzato.

Si potrebbe pensare che, comunque stiano le cose, non ci sia una grande differenza neanche tra i sistemi distribuiti e quelli centralizzati. In realtà però le differenze sono molto grosse e non subito evidenti.

Nei log dei sistemi distribuiti, fino al momento del merge, non vi è traccia dei cambiamenti avvenuti nel branch. Si tratta di un cambiamento positivo in quanto permette di tracciare una chiara linea di demarcazione tra i cambiamenti che appartengono a un ben determinato branch e quelli dell’albero principale. Sebbene inizialmente possa sembrare poco utile, non si può non notare come in realtà lo diventi nonappena si procede alla ricerca dei commit riguardanti una data funzionalità. Se questa è stata sviluppata in un branch, in un sistema centralizzato il risultato che otterremo sarà un’inversione temporale dei log, che quindi non saranno più correttamente ordinati considerando il momento del merge come momento iniziale dell’introduzione della nuova funzionalità, saranno invece mischiati temporalmente agli altri log ottenuti durante lo sviluppo di altri branch o del ramo principale. Questo aumenta la confusione e diminuisce drasticamente l’utilità dei log.

Un’ulteriore differenziazione, e questa di origine molto pratica, è la possibilità di eseguire con grande semplicità il cosiddetto merge forward. Capita praticamente sempre infatti che dopo la creazione di un branch, un altro venga unito nuovamente con il ramo principale e, nel caso di branch dalla vita molto lunga, possono capitare anche più fusioni tra branch. Per gestire questo tipo di necessità nei sistemi centralizzati bisogna ricorrere a strumenti di terze parti e/o al lavoro manuale di risoluzione dei conflitti tramite patch. Nei sistemi distribuiti invece basta eseguire un nuovo comando di aggiornamento dell’albero locale con l’albero remoto. Tutta la storia locale verrebbe fusa con quella principale (anche se non necessariamente, posso fondere con la stessa semplicità anche due branch distinti) sfruttando algoritmi estremamente sofisticati o strumenti esterni installati sul sistema in uso. In questa situazione, un ulteriore vantaggio dei sistemi distribuiti è, ancora una volta, la chiara separazione tra albero principale e alberi secondari relegando gli eventuali problemi al branch in sviluppo piuttosto che all’albero principale che deve rimanere relativamente stabile.

Sviluppo di software customizzati

Sviluppo di software customizzati

Figura 3. È possibile creare branch isolati e customizzati per ciascuno dei nostri clienti continuando a sviluppare il ramo principale e importanto solo i cambiamenti interessanti negli alberi specializzati.

Sull’onda del secondo modello analizzato arriva lo sviluppo di un software che viene poi adattato per ciascuno dei nostri clienti, anche solo perché vengono cambiati leggermente alcuni degli algoritmi implementati o i temi e i layout grafici. È immediata l’osservazione che riguarda la possibilità di isolare totalmente i singoli clienti (senza mantenerli nello stesso albero, magari dentro la directory branches/) mantenendo però un singolo albero principale, uguale per tutti, su cui avverrebbe lo sviluppo comune.

La semplicità di gestione del merge forward e la totale separazione dei branch rende gli strumenti distribuiti quelli più adatti a questo genere di situazioni lavorative.

Backport di funzionalità

Ancora una volta la semplicità di esecuzione del merge forward ci permette di avere alcune revision dei nostri software, anche chiamate tag, che vengono congelate in quanto rappresentano punti di riferimento nella storia di quel software. Capita spesso però di trovare problemi di sicurezza durante lo sviluppo e sarebbe estremamente utile avere la possibilità di aggiungere un tale update anche alle vecchie versioni rilasciate dei nostri prodotti. Nei sistemi distribuiti il problema viene semplicemente ridotto alla copia dei changeset interessanti per il fix all’interno dell’albero contenente il tag. Così facendo abbiamo aggiornato i nostri tag con le ultime patch di sicurezza e possiamo quindi aggiornare i software vecchi con il minimo sforzo. Nei sistemi centralizzati invece è richiesto un lavoro più complesso di estrazione del cambiamento, specialmente se consistente in più commit, e nell’integrazione dello stesso nel tag.

Esternamente a tutti e 4 i modelli vanno fatte notare ulteriori caratteristiche.

Soltanto gli utenti autorizzati possono eseguire un’operazione di push (spostamento dei changeset da locale a remoto), significa quindi che nel caso in cui uno sviluppatore non avesse tali permessi non sarebbe possibile proseguire in questa operazione ma sarebbe possibile procedere solo con l’aggiornamento del server con il repository remoto, eseguita da chi ha sufficienti permessi e che quindi può decidere se e come proseguire nel merge avendo pieno controllo sull’albero principale. Al limite estremo l’albero principale potrebbe essere accessibile solo allo sviluppatore principale o al responsabile del progetto che deciderebbe autonomamente quando e come unire le funzionalità sviluppate nei branch dei singoli sviluppatori.

Ciascun branch creato può quindi essere messo a disposizione del resto del mondo attraverso uno dei protocolli supportati dal software che decidiamo di usare, permettendo ai manutentori del progetto, su cui non abbiamo sufficienti permessi, di aggiornare i loro repository con i nostri cambiamenti, o addirittura ad aggiornare l’albero principale con i bugfix e le nuove funzionalità presenti nei repository dei membri della community.

Pubblicato il
6 Dic 2007
Tag

Commenti

  • Giovanni Intini il 14 Dic 2007

    Devo dare un +1 a tutto l'articolo di Valentino. Uso svn il 99% delle volte perché seguo sempre il "path of least resistance", ma ci sono decine di volte in cui la facilità di branching mi renderebbe la vita molto più semplice.

    Ho recentemente cominciato ad utilizzare git e mi ci trovo così bene che prima o poi faccio il salto nel buio e mollo svn.

  • Antonio Cangiano il 15 Dic 2007

    Giovanni, questo sembra essere un trend crescente nel mondo open source. Sempre più progetti lo adottano al posto di SVN.

  • Folletto il 17 Dic 2007

    Prima di fare il salto mi serve ancora che Mercurial, GIT, Bzr o altri facciano un filo più di strada nello stabilizzarsi degli ecosistemi che girano loro intorno. :)

    Peraltro, SVN ha anche il vantaggio di essere universale: ce l'ho come "one click install" dove ho l'hosting, ce l'ha Google e ce l'ha SourceForge. Scegliere fra Mercurial, GIT o Bzr è di per sé un ostacolo in più nell'adozione di un sistema che sembra oggettivamente migliore di SVN... :P

    Credo che non ci sia alcun "problema" se Hg implementasse "meglio" la connessione ad un repository SVN in modo trasparente: per SVN sarebbe un semplice nuovo "commit", mentre per l'Hg repository locale sarebbe un subtree. C'è un tool presente ma non mi sembra adeguatamente "trasparente". :)

    Vedo cmq che il rant si è sviluppato in uno splendido articolo, good work Valentino. :)

  • valentino il 17 Dic 2007

    Grazie Davide! L'integrazione con SVN e` indubbiamente un problema che andrebbe sistemato per bene. Pero` se le comunita` sono interessate a questa funzionalita` presto sara` supportata estremamente bene, non temere :).

  • Walter Franzini il 18 Dic 2007

    Valentino,

    il tuo e` un articolo interessante, ma mi sembra che nello svolgere le tue argomentazioni hai interpretato alcune delle cose che si possono fare _anche_ con un sistema distribuito come una caratteristica _esclusiva_ dei sistemi distribuiti.

    Nel seguito cerco di analizzare il tuo articolo mettendo in luce quelli che sono, secondo me, i punti deboli della tua argomentazione. Conduco la mia analisi facendo riferimento ad un sistema centralizzato reale, se avrai la pazienza di arrivare fino alla fine scoprirai di cosa si tratta.

    Quello che e` importante per lo "Sviluppo centralizzato" non e` tanto l'essere centralizzato o distribuito, bensi supportate la multiutenza.

    A tal riguardo sostieni sostieni che "non esistono particolari problemi strutturali che possano impedire ai sistemi distribuiti di funzionare in questo modo".

    Penso che concorderai con me nel classificare come distribuito un (ipotetico) sistema che supporta un (1) solo utente locale e che implementa come unica operazione tra repository la pull. Io sostengo che un tale sistema distribuito non e` adeguato ad essere usato nel modello "Sviluppo Centralizzato".

    Parlando del modello "Branch Based" la tua osservazione rispetto ai log "fino al momento del merge, non c'e` traccia dei cambiamenti ..." e` certamente vera, ma l'uso di un sistema distribuito non e` l'unico modo di ottenere questo effetto.

    In un sistema centralizzato (non ipotetico) i branch costituiscono un albero e finche il branch figlio non e` completo (committato) le modifiche introdotte non sono registrate nel branch padre.

    In un sistema centralizzato (non ipotetico) i branch costituiscono un albero e finche il branch figlio non e` completo (committato) le modifiche introdotte non sono registrate nel branch padre. Proseguendo con il nostro esempio, finche il branch figlio "1" non e` completo le sue modifiche non sono visibili agli altri figli "2", "3". Quando il branch "1" e` completo i file che ha modificato e non sono modificati nel branch "2" sono a questo immediatamente visibili. Nel caso ci siano file comuni tra il branch "1" e "2" questi sono marcati come da "riconciliare" con selezione automatica delle corrette version di file da utilizzare nell'operazione di merge. Prima del completamento del branch "2" tutti i file che ne hanno bisogno _devono_ essere riconciliati. Penso che quello che ho descritto sia piu` o meno quello che descrivi come "merge forward".

    Anche il caso della gestione di "Software customizzati" puo` essere gestito come indichi tu utilizzando lo stesso software centralizzato che ho menzionato.

    Per quello che riguarda le attivita` di backporting la vera differenza e` fatta dalla possibilita` replicare un changeset e non dalla possibilita` di avere piu` repository. Il concetto di changeset e` un grosso passo avanti rispetto ai sistemi in cui il commit puo` essere fatto a livello di singolo file in maniera indipendente ed e` essenziale per i sistemi distribuiti, ma non e` a loro esclusivo appannaggio. Ad esempio, dal 1998, prima di avere qualunque funzionalita` di distribuzione dei changeset, Aegis dispone del comando aeclone per copiare un changeset da un branch all'altro.

    Quello che ho scritto non e` una critica verso i sistemi distribuiti o verso il tuo articolo, ma vuole essere solo una precisazione al tuo articolo che ho trovato comunque stimolante. :-)

Screencast e videocorsi di programmazione
Stacktrace RSS Feed Stacktrace via E-mail
Hai idee per un articolo? Faccelo sapere!