Continuos Integration
Una pratica Agile che uso è la pratica del “Continuos Integration”, della integrazione continua. Le interazioni tra le persone avvengono abitualmente, ogni giorno e abitualmente ogni giorno il software cambia, perchè i requisiti cambiano, la tecnologia cambia, il design cambia, il business cambia, le persone del team cambiano, molte cose cambiano e componenti software vengono integrati.
Quando accade questo per mezzo della pratica agile di “Continuos Integration“, ciascuna integrazione del sistema, ciascuna integrazione del codice è verificata per mezzo di un processo di compilazione automatico, una build automatica che deve comprendere anche la verifica dei tests. Questo tipo di approccio consente di aver molti meno problemi nel momento in cui si rilascia in produzione.
Come è indicato nel nome Continuos Integration, l’integrazione non è una fase con limiti temporati definiti ma l’integrazione è continua nel senso che accompagna lo sviluppo del software, questo si realizza mediante questa pratica agile.
Essendo continua, nel caso di errori, nel caso di bugs si può facilmente e velocemente regredire a uno stato precedente funzionante.
Questa pratica agile si effettua per mezzo del controllo del repository del codice sorgente.
Continuos Integration è una delle pratiche che fanno parte dell’ eXtreme Programming.
Per realizzare Continuos Integration è necessario avere un Server dedicato all’integrazione (Continuos Integration Server).
I tools che attualmente utilizzo per Continuos Integration sono Jenkis o Hudson.
Implementazione del software e CI (Continuos Integration).
Lo sviluppatore ha una copia del codice sorgente sulla sua macchina (pc o laptop).
Tale “copia” del codice sorgente la si è ottenuta per mezzo di un Source Code Management System. Attualmente utilizzo Git, in passato ho utilizzato Subversion.
(A seguire una breve descrizione di come questi due diversi sistemi di versionamento funzionano.
Per avere sulla macchina locale il codice del progetto già esistente lo sviluppatore nel caso di Git avrà fatto un “clone”, nel caso di Subversion lo sviluppatore avrà fatto un “check out”. Per mandare le modifiche del proprio codice al server, lo sviluppatore nel caso di Git effettuerà un push (preceduto da un commit locale), lo sviluppatore nel caso di Subversion effettuerà un commit.
Un Source Code Management System ha il compito di gestire il codice sorgente in un repository su un Server. Il codice sorgente sul Server è principalmente in una directory chiamata mainline. Il codice sorgente sulla macchina locale è in una directory chiamata Working Directory, ovvero nella working directory si ha una Working Copy del codice sorgente.
Lo sviluppatore lavora sulla sua macchina in locale per sviluppare una nuova feature, o per modificarne una esistente, ecc.
Questa pratica agile ha come caratteristica il fatto che il codice sviluppato abbia dei tests automatici.
Io sviluppo in TDD, in Test Driven Development. Effettuata l’implementazione della feature/s effettuo la build sulla mia macchina locale, la build effettua operazioni di compilazione, di creazione di altre/nuove librerie software ed esegue i test automatici.
Effettuata la build e verificato che in locale i test automatici funzionano, allora si è a buon punto nel processo che consertirà il “commit” del codice sul Server (comando commit con Subversion, comando pull con Git).
Ora si aggiorna la propria copia in locale (comando Update in Subversion, comando pull in Git): questa fase consiste nell’aggiornare il codice in locale con il codice del Server (del repository), questo consente una nuova build nella macchina locale e una nuova esecuzione dei tests e se i tests sono senza errori allora si può effettuare un commit del codice sul server.
Nel caso invece di errori nella build o nei tests allora lo sviluppatore dovrà prima di committare il codice sul repository del Server effettuare un fix dei problemi e/o tests.
In Continuos Integrator vi è una macchina di integrazione, un Server di integrazione.
Questa macchina di integrazione deve avere accesso al repository del codice (assumiamo che sia la mainline del codice).
La macchina di integrazione effettua una nuova build, questa build viene eseguita con il codice sorgente del repository. Se questa build non ha errori ed è corretta (anche i tests devono essere senza errori ovviamente) allora solo in questo caso si può affermare che l’implementazione della feature è stata effettuata.
Questa build effettuata dalla macchina di integrazione è normalmente effettuata da un software, io utilizzo Jenkins oppure Hudson.
Può verificarsi il fatto che due o più sviluppatori effettuano un commit del codice sul server di versionamento quasi contemporaneamente, in questo caso il server di continuos integration avvertirà nel caso di errori della build o tests e quindi i problemi che potrebbero sorgere possono essere risolti velocemente e avere di nuovo una build funzionante.
Controlla i tempi:
il server di integrazione non dovrebbe avere una build non funzionante per più di 24/48 ore.
E’ stato descritto a un più alto livello possibile il concetto di Continuos Integration.
Continuos Integration viene attuato per mezzo anche delle seguenti pratiche.
Avere un solo repository del codice sorgente
(ovviamente l’Operation deve effettuare backups).
Si scrive il codice sorgente per effettuare la build del software. Il codice sorgente del team viene gestito con software che possono avere diversi nomi, ma che gestiscono fondamentalmente il codice sorgente, i nomi più comuni sono: Source Code Management Tool, Configuration management, version control system, revision control system, source control.
Il software di gestione del codice sorgente consente al team, e quindi al progetto la corretta gestione dei seguenti casi d’uso/eventi:
-
overwriting del codice: diversi sviluppatori possono lavorare in contemporanea sul progetto e non si ha overwriting di codice se non effettuato con consapevolezza.
-
history del source code: è possibile accedere alla storia del codice sorgente e visionare e quindi poi eventualmente modificare la storia dei files
-
scrivere le note di release del codice
-
avvertire quando il codice sorgente stesso è stato modificato
E’ buona norma prima dell’inizio del progetto accertarsi che tutti i componenti del team sappiano utilizzare il software di versionamento del codice (attualmente Git e Subversion sono i migliori).
E’ buona norma versionare tutto il progetto, quindi codice sorgente, files di properties, eventuali scripts come i database scripts, eventuali librerie aggiuntive, ecc. e non versionare il compilato, questo perchè ogni persona del team dovrebbe essere un grado di effettuare su una nuova macchina il check out del codice, ovvero prendere il codice sel server di versionamento e metterlo in una nuova macchina ed effettuare una corretta (e completa) build, quindi nel server di versionamento deve esserci tutto ciò che è necessario.
I sistemi di versionamento consentono anche di creare branches del codice, il consiglio è quello di crearli solo se è veramente necessario.
Automatizza la build
Automatizza tutto quello che può essere automatizzato. La build può effettuare diverse operazioni, come la compilazione dei sorgenti, la CRUD di directory,ecc. tutte queste operazioni sono automatizzabili e quindi si devono automatizzare, poichè è possibile commettere errori in procedure non automatizzate.
Qualsiasi persona del team come scritto precedentemente dovrebbe essere in grado di prendere il codice sorgente del server di versionamento e per mezzo di un solo comando effettuare una corretta build.
Spesso una build completa richiede tempo ma spesso si utilizza un process building che è in grado di controllare la data del sorgente e quindi se con la nuova build il sorgente deve essere di nuovo compilato oppure no, questo consente un notevole risparmio di tempo nel processo di build. Inoltre il file di build dovrebbe consentire diversi target a seconda delle necessità.
Lo sviluppo viene effettuato per mezzo di un IDE (Integrated Development Environment, Eclipse è il mio preferito per Java ad esempio), molto spesso la build effettuata per mezzo di un IDE non consente la visibilità su problemi che potrebbero esserci in una build fatta bene da console (ad esempio perchè alcuni files sono nel classpath dell’IDE e non nel classpath dello script di build,ecc.).
Quindi è molto importante avere sempre una build funzionante che è eseguibile dal Server.
Includi i tests nella tua build
La build può effettuare diverse operazioni, tipicamente compilazione di sorgenti, creazione di librerie, spostamento e creazioni di directories, ecc.
Può esserci una build senza errori, ma un approccio agile prevede che ci siano i tests unitari, ovvero nello script di build devono esserci i tests di unità e devono essere dalla build stessa eseguiti e non dare errori.
Se si è sviluppato in TDD (Test Driven Development) si hanno test di unità più efficienti ed efficaci poichè si sono stati scritti prima del codice applicativo.
In ogni caso anche se non si è sviluppato in TDD si dovrebbero avere dei tests automatici e sarebbe ideale che tutti i tests fossero eseguibili con un unico comando.
I commits devono essere consistenti e frequenti: piccole funzionalità implementate in modo incrementale.
L’integrazione consente anche agli altri membri del team una consapevolezza delle funzionalità implementate. E’ importante che i commits effettuati nel repository oltre che aver un corretto design, che abbiano una build e i tests funzionanti.
Di seguito il corretto flusso che gli sviluppatori dovrebbero seguire finito di implementare una funzionalità e prima dell’effettivo commit del codice:
-
chiedersi se si ha una build nell’IDE funzionante
-
chiedersi se si ha una master build da console funzionante
-
i test unitari si eseguono senza errori?
-
effettua un update del codice dal server
-
risolvi eventuali conflitti con la mainline
-
riesegui la build
-
verifica che la build sia funzionante
-
verifica che tutti i tests siano senza errori
-
commit del codice scrivendo un commeto identificativo della funzionalità implementata
I commits frequenti consentono anche nel caso di problemi o bugs una veloce soluzione.
Una buon approccio è effettuare il commit solo se si è implementata una funzionalità o una sotto funzionalità correttamente e consistente.
Inoltre frequenti commit aiutano gli sviluppatori a sviluppare semplici funzionalità. La Semplicity è una della caratteristiche dell’ eXtreme Programming (XP).
Questo consente anche una maggior sicurezza e conoscenza del software che si sta sviluppando.
Ciascun commit nel repository dovrebbe essere seguito da una nuova build sulla macchina di integrazione (Continuos Integration Server).
Come abbiamo detto precedentemente lo sviluppo è fatto su macchine locali, i commits sul repository su un’altra macchina (che abbiamo definito Server per comodità) sul quale c’è il repository (la mainlaine del codice). Ecco che la macchina di integrazione deve effettuare un checkout del codice dopo ogni singolo commit ed effettuare una nuova build e quindi verificare che la build sia corretta (ovviamente anche i test devono essere corretti). Avere una build funzionante di ambienti diversi consente di aver un maggior controllo e consapevolezza del codice.
E’ importante essere consapevoli che solo se la build della macchina di test viene creata senza errori allora solo in quel caso la funzionalità è da considerarsi implementata.
La build sul Server di integrazione può essere in due modi e uno è preferibile all’altro.
Un modo è che uno sviluppatore si colleghi in remoto sul server di integrazione ed effettuti il check out del progetto ed effettui la build manualmente.
Un altro modo che è quello consigliabile è utilizzare un software come Jenkis per l’integrazione, ovvero dopo ogni commit effettuerà un check out del progetto ed effettuerà una nuova build, e a seconda di come è stato configurato il software, il Server di integrazione avvertirà via email dello stato nella nuova build (se ha avuto successo oppure no). Alcuni preferisco configurarlo a seconda dell’opposto dell’ Hollywood principle. L’ Hollywood principle dice: “don’t call us, we’ll call you”, ovvero non chiamarci, ti chiameremo noi. Portato al concetto informatico di Continuos Integration, l’opposto consiste nel fatto che se la build non ha successo si avvertono gli utenti, o meglio, in questo caso gli utenti di un software di CI (ad esempio Jenkis) sono gli sviluppatori, si avvertono gli sviluppatori per mezzo di una email (ovviamente lo fa in automatico il software di integrazione, una volta configurato).
Alcuni configurano il server di integrazione per una sola build al giorno e magari notturna, anche se è meglio avere una integrazione continua che non averla, in realtà è meglio configurare una nuova build, quindi una nuova integrazione ogni volta che il codice della mainline è cambiato, questo è effettuare Continuos Integration.
Mantieni veloce la build.
Spesso si possono creare situazioni per la quale la master build necessità di tempo e qundi non è più una build veloce. Il consiglio è avere una build veloce. Questo consente agli svilluppatori stessi di non annoiarsi mentre eseguono la build e inoltre non si devo rifocalizzarsi sul codice, cosa che deve avvenire in caso di build lunga.
La master build non dovrebbe mai essere più lunga di 10 minuti, naturalmente è auspicabile che sia al massimo di due o tre minuti.
Nel caso di build lunghe e di commit frequenti e ravvicinati si potrebbero avere dei problemi, è importante conoscere bene anche il software di Continuos Integration.
Avere un ambiente di Test/Collaudo/Preproduzione (può essere chiamato in diversi modi) il più simile, meglio ancora se è il clone dell’ambiente di produzione.
In questo ambiente si dovrebbe avere qualsiasi tipo di problema che si ha nell’ambiente di produzione. Se i tuoi test hanno un diverso risultato in ambienti diversi allora non sono test scritti bene, i tests dovrebbero avere lo stesso risultato in ambienti diversi.
Si vede aver cura che sia la versione del database, sia la versione del sistema operativo sia la stessa dell’ ambiente di produzione, stesse librerie e stessa versione delle librerie software utilizzate e stesso hardware.
Per mezzo della virtualizzazione è abbastanza semplice effettuare cloni degli ambienti.
Semplicity.
Cerca di essere e mantenere il processo più semplice possibile.
Spesso le pensone sono focalizzate su cosa non funziona invece che su cosa funziona, lo sviluppo agile consente rapidi cambiamenti e questo vuol dire anche la possibilità di effettuare rapidi bug fixing.
Qualsiasi componente del team dovrebbe essere in grado di prendere l’ultima build ed eseguire l’applicazione, gli scopi possono essere molteplici.
La comunicazione è importante.
La comunicazione ha un ruolo importante in CI (Continuos Integration), quindi ogni componente del team dovrebbe essere in grado di visionare i cambiamenti effettuati sui server ed essere in grado di effettuare eventuali configurazioni.
Inoltre i software di Continuos Integration mettono a disposizione pagine web per mezzo delle quali può vedere mantenere monitorato lo stato delle varie build e dei vari logs, l’accesso allo stato delle build e dei logs è messo a disposizione ad ogni membro del team. Questo consente anche al team di aver maggior visibilità e conoscenza sulla build e sullo stato del software.
Il deployment deve essere automatizzato.
Continuos Integration prevede il trasferimento di files in macchine diverse, quindi si deve automatizzare questo processo e inoltre è importante che si abbia uno script di deploy che consenta a chiunque del team di effettuare il deploy in modo semplice, l’ideale sarebbe con un solo comando. Di conseguenza anche in produzione il rilascio può essere effettuato in modo semplice con un solo comando.
Il deployment automatico in CI consente in produzione una velocità di rilascio e una significativa riduzione di possibili errori.
Poichè l’ambiente di Test/Collaudo/Preproduzione dovrebbe essere il clone dell’ambiente si produzione, allora anche il rilascio in produzione dovrebbe essere simile a quello dell’ambiente di Test/Collaudo/Preproduzione.
Il processo di build automatizzato ed intervalli regolari permette di verificare se i cambiamenti di codice fanno fallire test che prima erano funzionanti, se i test hanno successo si “tagga” la versione del codice.
Conclusione: I vantaggi di CI (Continuos Integration)
Continuos Integration ha diversi vantaggi di:
-
ridurre gli errori
-
ridurre i rischi
-
permette una maggior confidenza e conoscenza del software a tutti i membri del team
- i bugs sono molto più velocemente identificabili e risolvibili.
- i bugs sono anche cumulativi, questo significa che più bugs si hanno e più e difficile risolvere quel bug specifico, poichè quel bug è dato dalla sommatoria di altri bugs ein questo caso anche gli sviluppatori psicologicamente sono meno disposti a risolvere quel bug che è dovuto ad altri bugs. CI consente di ridurre i bugs in modo significativo
-
si hanno deployment frequenti, con tutti i relativi benefici
-
si amministra e si automatizza il processo di build
-
si integra il lavoro del singolo nel lavoro del team
-
si hanno feedbacks sui cambiamenti di codice, ma anche report, documenti
-
si diminuiscono considerevolmente i problemi di integrazione
Alessandro Ceseno
PS: se hai domande scrivimi pure per mezzo della sezione contatti.