Impostazione di un processo di Continuous Development per il

Transcript

Impostazione di un processo di Continuous Development per il
 Dipartimento di Matematica e Informatica
Corso di Laurea in Informatica
Impostazione di un processo di
Continuous Development per il portale
web Volagratis.it
Candidato: Corrado Lombardi
Relatore: Dott. Ing. Giulio Destri
Correlatore: Stefano Salmaso
Anno Accademico 2013/2014
2
Ad Adelaide, per avermi supportato e sopportato.
Ad Alice e Aurora, che non capiscono perché papà “Vada ancora a Scuola”.
Ai miei genitori, scusandomi per il ritardo.
Ai miei amici di sempre, punto di riferimento insostituibile.
Agli amici e colleghi dello Scrum Team “Commander” a cui appartengo: Nadia, Stefania,
Massimo, Davide, Giuseppe, Stefano e Vittorio. Senza di loro non esisterebbe questa tesi.
Ai miei colleghi dell’IT Volagratis, per lo stesso motivo di cui sopra.
A chi, anni fa, mi ha convinto a tentare la carriera di studente-lavoratore.
Un ringraziamento particolare al relatore, Dott. Giulio Destri, per la collaborazione, la
pazienza e la disponibilità, anche ad orari “non convenzionali”.
3
4
Indice
Indice
1. Introduzione ...................................................................................................................... 1
2. Il ciclo di vita del software ................................................................................................ 3
Ingegneria del software: 1968… e poi?............................................................................. 3
I modelli tradizionali dello sviluppo software ................................................................... 4
Il ciclo di vita del software ................................................................................................ 8
La fase di progettazione (Design) ................................................................................ 10
La fase di implementazione (Coding) .......................................................................... 10
La fase di Test .............................................................................................................. 11
La fase di avvio ed entrata in produzione .................................................................... 11
La fase di Manutenzione .............................................................................................. 12
Le esigenze dei sistemi odierni........................................................................................ 12
I limiti dei processi di sviluppo tradizionali .................................................................... 13
Fase di test tardiva ....................................................................................................... 13
Burocrazia .................................................................................................................... 13
Attività svolte controvoglia e/o sotto scadenza ........................................................... 14
Regressione .................................................................................................................. 14
3. Agile Programming ......................................................................................................... 17
Cosa significa Agile? Perché è vantaggioso? .................................................................. 17
Ruoli Agili ................................................................................................................... 18
Agile manifesto e Agile programming ............................................................................ 19
Superare i limiti dello sviluppo tradizionale ................................................................... 21
L’Extreme Programming ................................................................................................. 24
Pianificazione (Planning Game) .................................................................................. 24
Gestione ....................................................................................................................... 25
Design .......................................................................................................................... 26
Coding.......................................................................................................................... 26
Testing ......................................................................................................................... 27
User Stories .................................................................................................................. 27
Test Driven Development (TDD) ................................................................................ 28
Valori ispiratori dell’XP .............................................................................................. 29
Un caso di successo: SCRUM ......................................................................................... 30
5
Indice
Ruoli ............................................................................................................................ 31
Metodologia di lavoro ................................................................................................. 31
4. Continuous Development ............................................................................................... 39
Il processo di Continuous Development .......................................................................... 39
Continuous Integration ................................................................................................ 39
Continuous Delivery ................................................................................................... 39
Continuous Deployment ............................................................................................. 40
Cosa serve per impostare il processo di Continuous Deploy........................................... 42
Unico repository per i sorgenti.................................................................................... 42
Automatizzare la Build ............................................................................................... 43
Rendere la Build Auto-Testante .................................................................................. 44
Tutti committano sulla mainline ogni giorno .............................................................. 44
Eseguire tutti i Commit Tests localmente prima di committare ................................. 45
Ogni Commit lancia una build della mainline su una Integration Machine ............... 45
Non iniziare altre attività fino a quando non vengono superati i Commit Tests......... 46
Non committare se la build è rotta .............................................................................. 46
Mai andare a casa se la build è rotta ........................................................................... 46
Sistemare immediatamente le build rotte .................................................................... 47
Essere sempre pronti a tornare alla versione precedente ............................................ 47
Definire un Time-Box per il fix della build rotta ........................................................ 48
Mantenere la build rapida ........................................................................................... 48
Testare in un clone dell’ambiente di Produzione ........................................................ 49
Non escludere i test che falliscono .............................................................................. 50
Assumersi le proprie responsabilità ............................................................................ 50
Rendere gli ultimi eseguibili creati accessibili facilmente .......................................... 50
Chiunque è informato sulla build ................................................................................ 51
Automatizzare il Deployment ..................................................................................... 51
Da dove cominciare? ................................................................................................... 52
I vantaggi ......................................................................................................................... 53
Vicolo Cieco ............................................................................................................... 53
Bugs ............................................................................................................................ 53
L’evoluzione ulteriore: DevOps ...................................................................................... 54
5 . L'impostazione del processo in Volagratis .................................................................... 57
Architettura del sistema ................................................................................................... 58
6
Indice
AS-IS: il punto di partenza .............................................................................................. 61
Applicazione web Volagratis ....................................................................................... 62
Applicazione web services........................................................................................... 63
Business Scanner Architecture (BSA) ......................................................................... 63
Spiderfly ...................................................................................................................... 64
Integrazione Sistemi GDS ........................................................................................... 65
TO-BE: cosa vogliamo ottenere ...................................................................................... 65
I passi di impostazione .................................................................................................... 66
6. Attività operativa............................................................................................................. 69
Scelta del modulo candidato............................................................................................ 69
Scelta dei test e motivazioni ............................................................................................ 70
Test Unitari .................................................................................................................. 70
Smoke Test .................................................................................................................. 72
Test End2End............................................................................................................... 72
Integration Test ............................................................................................................ 73
Scelta degli strumenti ...................................................................................................... 73
Subversion ................................................................................................................... 73
Maven .......................................................................................................................... 74
Jenkins ......................................................................................................................... 75
JUnit ............................................................................................................................. 76
Cucumber ..................................................................................................................... 76
Mockito ........................................................................................................................ 77
Ansible ......................................................................................................................... 78
Il processo nella pratica ................................................................................................... 79
Tomcat e Subversion ................................................................................................... 79
Processo di Build ......................................................................................................... 80
Test con Mockito e Cucumber ..................................................................................... 81
Script di deploy su server di sviluppo .......................................................................... 83
Jenkins Pipeline ........................................................................................................... 84
Non tutto automatizzato ............................................................................................... 87
Obiettivi raggiunti ........................................................................................................... 88
Feedback Rapido ......................................................................................................... 88
Deploy automatico ....................................................................................................... 88
Miglioramento efficienza test ...................................................................................... 89
7
Indice
War unico .................................................................................................................... 89
Difficoltà incontrate ......................................................................................................... 90
7. Conclusioni..................................................................................................................... 91
Il lavoro svolto ................................................................................................................. 91
Vantaggi ottenuti ............................................................................................................. 92
Lezione appresa ............................................................................................................... 94
Prospettive future ............................................................................................................. 95
Refactoring e copertura test ........................................................................................ 95
Sistema modulare ........................................................................................................ 95
Indice delle figure............................................................................................................... 97
Bibliografia......................................................................................................................... 99
8
1. Introduzione
1. Introduzione
Il problema di coesistenza di sviluppo, deployment,
manutenzione in sistemi mission-critical e/o di e-commerce
Negli ultimi anni l’elevata concorrenza mette il time to market tra i fattori determinanti per
il successo di un prodotto software. Per questo le moderne aziende di sviluppo software
hanno necessità di ridurre al minimo il tempo che intercorre tra la richiesta del committente
(esigenza di Business) ed il rilascio del prodotto software che la soddisfa. Contestualmente,
la qualità del software deve rimanere alta. Il rilascio di software contenente bug o
malfunzionamenti potrebbe causare problemi.
Questa esigenza è sentita anche in aziende che non sono propriamente aziende di sviluppo
software, ma che basano sul software e sulla tecnologia la maggior parte della propria
attività. Tipico esempio sono i portali e-commerce. Queste aziende hanno anche la necessità
di assicurare la continuità del servizio erogato. Una interruzione, anche della durata di pochi
minuti, causa danni importanti.
A fronte di queste esigenze, i sistemi di sviluppo software tradizionali come, ad esempio, il
modello a cascata (Waterfall) presentano dei limiti. Secondo il modello a cascata, ad
esempio, le fasi attraversate da un progetto software (Analisi, Implementazione, ecc.)
devono rispettare un ordine rigoroso e vengono eseguite in sequenza, una fase non inizia se
la precedente non è completamente terminata. Un rigore di questo tipo finisce
inevitabilmente per allungare i tempi di rilascio del prodotto col rischio di ridurne le
potenzialità e la remunerazione degli investimenti sostenuti, qualora un concorrente arrivi
prima sul mercato con un prodotto simile.
La presente tesi affronta queste problematiche e descrive il processo che ha portato l’azienda
che gestisce Volagratis, un portale web a largo traffico, ad implementare una strategia di
Continuous Development.
Per prima cosa sono state adottate metodologie di lavoro Agili come Scrum ed Extreme
Programming che favoriscono i rilasci frequenti ed incrementali di prodotto, danno massima
importanza alla fase di scrittura del codice, a discapito di quella di analisi, che può essere
raffinata, in parallelo alla prima e raccomandano la predisposizione di adeguati test
automatici a verifica e copertura del codice scritto. La fase di testing è integrata in parte con
quella di scrittura del codice, e non interamente demandata alla fase pre rilascio in
produzione.
Successivamente è stata implementata una Pipeline per gestire un processo di Continuous
Delivery su uno dei moduli che fanno parte dell’architettura del sistema.
1
1. Introduzione
Lo scopo di questa pipeline è quello di mantenere controllato lo stato della versione di
riferimento (codebase) di un progetto software e fornire in output un pacchetto rilasciabile
con un operazione semplice come un click del mouse o l’esecuzione di uno script.
Il rilascio automatizzato di un’applicazione la cui solidità è garantita dalla presenza di test
in prevalenza automatici, svolti frequentemente, riduce sensibilmente il carico di lavoro e
di stress conseguente che le operazioni di rilascio causano ai team di sviluppo. Questo
permette di effettuare i rilasci frequenti ed incrementali di prodotto, raccomandati dalle
metodologie Agili e quindi a rilasciare in tempi ridotti software che risponda alle richieste
di Business.
L’inserimento degli strumenti per il Continuous Development and Deployment in azienda
ha portato ad ottimi risultati, che saranno presentati nei capitoli finali.
2
2. Il ciclo di vita del software
2. Il ciclo di vita del software
Ingegneria del software: 1968… e poi?
Nel 1968 la conferenza NATO tenuta a Garmisch, in Germania, rende chiaro il problema
rappresentato dall'incapacità di produrre nei tempi previsti software affidabile e rispondente
ai requisiti (da Wikipedia [1]).
Possiamo dire che l’ingegneria del software nasce allora. Ed è a cavallo di questi anni che
vengono ideati i primi modelli di sviluppo software, molti dei quali ispirati da processi e
procedure in essere in altri settori dell’industria del tempo, soprattutto i settori edile e
manifatturiero.
Ma poi? Cosa è successo? Quanto i metodi sviluppati negli anni immediatamente successivi
alla conferenza NATO hanno contribuito a risolvere i problemi emersi?
In questo capitolo viene presentato e descritto brevemente il primo modello formalizzato in
quegli anni, ovvero il modello a cascata e vedremo come e quanto questo modello si sia
evoluto nel corso degli anni.
Analizzeremo anche quali sono le esigenze dei moderni sistemi software e ci chiederemo se
i modelli tradizionali di sviluppo possono soddisfare queste esigenze.
Vedremo che, inevitabilmente, a fronte di queste esigenze emergono dei limiti di questi
modelli di sviluppo. La domanda, quindi, è: “Si può fare di meglio?”.
3
2. Il ciclo di vita del software
I modelli tradizionali dello sviluppo software
Nei modelli tradizionali, lo sviluppo software avviene attraverso una successione di fasi
differenti, l’output di ogni fase costituisce l’input della successiva.
Si tratta di modelli iterativi che, solitamente, prevedono che una fase non possa iniziare se
prima non è terminata quella che la precede.
Il modello tradizionale per eccellenza è il modello a cascata (Waterfall) la cui prima
formale descrizione è riportata in un articolo del 1970 di Winston W. Royce [2]. La prima
definizione del modello come modello a cascata appare invece in uno scritto del 1976 di
Thomas E. Bell e T.A. Thayer [3]
Questo modello ha origine in industrie manifatturiere ed edili, settori altamente strutturati
nei quali modifiche in corsa al prodotto (after-the-fact) sono fattibili, ma a costi molto
elevati. Ovvio pertanto che venga posto il massimo rigore sulle fasi di analisi e
progettazione.
Dal momento che, agli albori dello sviluppo software, non erano stati “pensati” modelli atti
allo scopo, non si fece altro che mutuare questo modello iterativo in uso nelle imprese
manifatturiere ed implementarlo nello sviluppo software.
Il modello prevede fasi successive: Raccolta ed analisi requisiti, Design, Implementazione,
Integrazione, Test, Installazione e Manutenzione.
Figura 1 Grafico del modello a cascata
Le regole di implementazione del modello a cascata stabiliscono che una buona percentuale
del tempo dedicato al progetto deve essere investito nelle fasi di raccolta ed analisi dei
requisiti. Tipicamente viene investito il 20-40% del tempo a disposizione nelle fasi di
4
2. Il ciclo di vita del software
Raccolta ed analisi dei requisiti ed in quella di design. Il 30-40% viene dedicato alla fase di
implementazione ed integrazione, il resto del tempo è dedicato a test ed installazione.
L’idea centrale è che il tempo investito precocemente per assicurare che tutti i requisiti
vengano correttamente rispettati comporta notevoli benefici in seguito. In termini monetari,
prevenire un bug durante una delle fasi iniziali (Requisiti o Design) comporta uno sforzo
economico enormemente minore rispetto al risolverlo durante una fase avanzata come ad
esempio Implementazione o Test (dalle 50 alle 200 volte più oneroso).
Pertanto la metodologia rigorosa di sviluppo a cascata stabilisce che una fase non possa
iniziare se quella che la precede non è completa al 100%.
Sulla base del modello a cascata sono nati altri modelli che ne condividono le linee guida,
seppur con variazioni più o meno leggere, quali, ad esempio, la possibilità di ritornare alla
fase precedente o addirittura alla fase di design, qualora emergano criticità durante la fase
in corso tali da ostacolarne il progresso.
Figura 2 Modello modificato con possibilità ritorno a fase precedente
Nel suo waterfall final model, W. Royce illustrò che il feedback raccolto durante la fase di
Test, potrebbe evidenziare problemi tali da raccomandare un ritorno alla fase di Design ed,
eventualmente, da questa alla raccolta o all’analisi dei requisiti, qualora sia necessaria una
rivisitazione dei requisiti per risolvere questi problemi.
5
2. Il ciclo di vita del software
Figura 3 Modello modificato, con possibile ritorno alle fasi di Design ed Analisi dei requisiti
Tre varianti del modello a cascata vengono presentate in [4]
La prima è il Waterfall with overlapping phases (Sashimi). Questo modello preserva la
suddivisione in fasi, ma rende meno netto il confine tra di esse, introducendo una sorta di
esecuzione parallela, almeno in parte, tra le fasi. Modello ideale per progetti nei quali le idee
circa requisiti e lavoro da svolgere non sono chiari all’inizio, ma lo divengono a lavorazione
in corso.
Figura 4 Modello Sashimi
Sempre in tema lavorazione parallela, viene presentato il modello Waterfall with
subprojects la cui idea chiave è la suddivisione di un progetto in N sottoprogetti a
6
2. Il ciclo di vita del software
lavorazione parallela dopo una prima fase di Design Architetturale e vengono uniti assieme
prima della fase di Test generale del sistema.
Figura 5 Waterfall with subprojects
L’ultimo esempio presentato è invece focalizzato sul tema della riduzione dei rischi ed è il
Waterfall with risk reduction. In questo modello alcune fasi, tipicamente analisi requisiti
e design, vengono svolti secondo un approccio a spirale (risk reduction spiral). L’approccio
a spirale prevede una evoluzione incrementale di un progetto o di una o più fasi di
lavorazione. Ad esempio, in uno sviluppo a spirale, un progetto viene creato tramite
lavorazioni a complessità incrementale.
Figura 6 Risk reduction spiral (da [4])
7
2. Il ciclo di vita del software
Figura 7 Modello risk reduction: le aree colorate indicano fasi che tipicamente sono svolte con approccio
a spirale
Il ciclo di vita del software
Con il termine “ciclo di vita” del software ci si riferisce alle fasi attraversate da un progetto
software durante la sua evoluzione, dall’idea iniziale alla manutenzione. Il risultato finale è
il prodotto software stesso, accompagnato da tutta la documentazione ad esso associata.
In [5] vengono elencate e descritte le fasi comprese nella struttura base di un progetto
software, ne riportiamo un estratto.
Le fasi, che rispecchiano quanto definito dal modello a cascata, sono:
 Raccolta dei requisiti
 Analisi
 Progettazione
 Implementazione
 Test
 Installazione in produzione
 Manutenzione (ordinaria ed evolutiva)
e vengono così descritte.
8
2. Il ciclo di vita del software
Raccolta dei requisiti ed analisi
Il progetto inizia con la fase di raccolta dei requisiti.
In questa fondamentale fase vengono adottate e combinate tra loro diverse tecniche volte ad
“estrarre dalla mente del cliente o del committente tutto ciò che il progetto software dovrà
fare”.
Il “Manuale dell’Analista” [6] definisce questa fase come requirement elicitation, che può
essere svolta con una combinazione di 12 metodi diversi quali ad esempio: Interviste,
Osservazioni sul campo, Gruppi di Lavoro, ecc.
Il termine elicitazione è mutuato dalla psicologia e significa “tirare fuori” informazioni, in
questo caso, mediante domande o altri comportamenti stimolanti.
Alla fase di raccolta requisiti segue quella di analisi. Fase estremamente importante in
quanto, un errore commesso a questo punto, può avere ripercussioni molto serie sul risultato
finale.
Figura 8 Impatto degli errori fatti in fase di raccolta delle specifiche sulle fasi successive.
A seconda delle dimensioni del progetto l’analisi può essere svolta in toto all’inizio, oppure
iterativamente durante il processo. A seconda del modello di sviluppo che viene adottato,
l’analisi può essere ripetuta o meno durante il ciclo di vita del software.
L’obiettivo della fase di analisi è la descrizione completa e formalizzata, con un livello di
dettaglio adeguato di tutto ciò che il sistema deve fare (requisiti funzionali), dell’ambiente
in cui dovrà operare (requisiti non funzionali) e dei vincoli che dovrà rispettare. Notare che
la descrizione specifica cosa il sistema dovrà fare, non il come (modello a scatola nera).
Queste descrizioni vengono raccolte in documenti chiamati documenti di specifica,
brevemente specifiche.
9
2. Il ciclo di vita del software
La fase di progettazione (Design)
Partendo dall’output della fase di analisi, che deve essere preciso e privo di ambiguità, la
fase di progettazione definisce le istruzioni operative per la realizzazione del progetto
(dettagli implementativi). Le istruzioni devono avere il giusto livello di dettaglio ed essere
raccolte in documenti opportunamente strutturati. Pertanto, la progettazione di
un’applicazione è composta dalle attività per individuare la soluzione implementativa
migliore rispetto agli obiettivi funzionali, a quelli non funzionali ed ai vincoli. Queste
attività possono essere di varia natura, possono essere svolte in tempi e modi diversi in base
all’approccio seguito, ma in generale aiutano progettisti e team di sviluppo a prendere
decisioni importanti, spesso di natura strutturale.
Il risultato della progettazione è la definizione dell’architettura del sistema, intendendo con
questo termine l’organizzazione strutturale del sistema stesso, che comprende i suoi
componenti software, le proprietà visibili esternamente di ciascuno di essi (l’interfaccia dei
componenti) e le relazioni fra le parti.
In analisi, requisiti e struttura del sistema sono rappresentati in forma astratta e
(teoricamente) indipendente dalla tecnologia. La progettazione tiene conto anche di tutti i
fattori relativi all’utilizzo di una tecnologia concreta e quindi, a differenza dell’analisi, la
progettazione non può essere svolta indipendentemente dalla tecnologia utilizzata.
Durante la progettazione devono anche essere definiti tutti gli aspetti necessari per una
implementazione non ambigua.
Uno strumento molto usato nella progettazione è il diagramma di flusso o flow-chart, oppure
la sua evoluzione UML activity diagram o diagramma delle attività. Ambedue gli strumenti
servono a realizzare la scomposizione delle attività da compiere in elementi sempre più
piccoli che possano essere facilmente implementati con opportuni insiemi di istruzioni del
linguaggio di programmazione scelto.
La fase di implementazione (Coding)
La scrittura del codice sorgente può essere svolta con molti strumenti, il più semplice dei
quali è l’editor di file ASCII di base (notepad, gedit, vi, …). Per la complessità che i moderni
linguaggi ad oggetti richiedono però tale approccio è troppo poco produttivo. Dovendo
infatti garantire il rispetto di tempi stretti, è necessario disporre di tutte le funzioni di
facilitazione integrate entro un unico strumento per la scrittura del codice sorgente.
Un Integrated Development Environment (IDE), è un software che aiuta i programmatori
nello sviluppo del codice. Normalmente consiste in un editor di codice sorgente, un
compilatore e/o un interprete, un tool di building automatico, e (solitamente) un debugger.
Ormai sempre più spesso è integrato con sistemi di controllo di versione (SVN o GitHub,
ad esempio), con sistemi di gestione delle dipendenze da librerie esterne (Maven) e con uno
o più tool per semplificare la costruzione di una GUI.
Alcuni IDE, rivolti allo sviluppo di software orientato agli oggetti, comprendono anche un
navigatore di classi, un analizzatore di oggetti e un diagramma della gerarchia delle classi.
10
2. Il ciclo di vita del software
Sebbene siano in uso alcuni IDE multi-linguaggio, come Eclipse, NetBeans, IntelliJ Idea e
Visual Studio, generalmente gli IDE sono rivolti ad uno specifico linguaggio di
programmazione.
La scrittura del codice sorgente, per quanto spesso poco considerata, rimane comunque la
fase fondamentale di ogni progetto informatico. L’analisi e la progettazione possono essere
state svolte al meglio, ma, se il codice viene scritto male, l’applicazione risultante avrà
problemi e funzionerà male. Facendo un’analogia nell’ambito dell’ingegneria civile, è
chiaro che anche il progetto più bello, se i pilastri non sono fabbricati bene, se i mattoni non
sono posati con accuratezza o se i materiali impiegati sono di scarso pregio, darà origine ad
un edificio di pessima qualità.
Anche per questo, metodologie di programmazione più moderne, tendono a fare diventare
la fase di scrittura del codice quella principale di tutto il progetto informatico.
Durante la stesura del codice ha luogo anche il primo debugging, ossia la rimozione degli
errori di sintassi e degli errori più evidenti, come la mancata inizializzazione di variabili,
che possono compromettere la compilazione o il funzionamento del programma. Gli IDE
più moderni hanno ottimi sistemi di live debug. Tendono ad evidenziare in tempo reale, oltre
al codice che inevitabilmente porterà ad errori di compilazione, anche frammenti di codice
inutili, blocchi ripetuti, inizializzazioni di variabili non necessarie, etc. etc.
La fase di Test
Una volta che il programma è stato completato o comunque può iniziare a funzionare,
occorre verificare che il suo funzionamento sia conforme a tutte le specifiche che erano state
stabilite nella fase di analisi. Questo è lo scopo fondamentale della fase di test.
Gli errori che portano al non rispetto delle specifiche sono di solito molto più insidiosi da
scoprire rispetto a quelli che vengono trovati durante il debugging. Non sono errori di
sintassi, ma errori logici e concettuali, come, per esempio, lo scrivere il segno ‘+’ invece del
segno ‘-‘ entro un algoritmo che richieda la sottrazione e non la somma.
La fase di test prevede quindi di verificare il comportamento effettivo del programma
rispetto a quello previsto e di segnalare le differenze di comportamento ai programmatori
che dovranno procedere alla ricerca e alla eliminazione delle cause di tali differenze.
I modelli di sviluppo software più moderni pongono l’accento sulla fase di test,
anteponendola in alcuni casi alla fase di sviluppo (ad esempio il Test Driven Development,
TDD) e prevedendo l’esistenza di suite di test eseguite automaticamente durante la build ed
il packaging del progetto, allo scopo di ridurre il più possibile il rischio di regressione.
La fase di avvio ed entrata in produzione
Dopo il test, raggiunto un livello sufficiente di qualità, il programma può entrare in
produzione. Questo termine ha un significato diverso secondo il tipo di programmi in
realizzazione.
11
2. Il ciclo di vita del software
Per i programmi destinati alla vendita presso il pubblico, o alla distribuzione se gratuiti,
questa fase rappresenta il rilascio sul mercato, fisico (negozio), virtuale (e-commerce,
download) o mobile (PlayStore, AppStore) che sia. Per i programmi realizzati
specificatamente per un cliente (i cosiddetti “programmi custom”), questa fase rappresenta
l’installazione ed il collaudo presso la sede del cliente che li ha richiesti. In ultimo, per le
applicazioni web (siti di e-commerce, portali, gaming-on-line, …) rappresenta
l’installazione ed il collaudo su uno o più server web (Apache, Tomcat, IIS, ...) ed il tuning
di questi ultimi.
Al termine di questa fase i programmi iniziano la propria vita operativa, durante la quale
svolgono il compito previsto nel contesto per cui sono stati progettati, che può proseguire
anche per molti anni.
La fase di Manutenzione
Durante la vita operativa possono verificarsi necessità di interventi correttivi o di
aggiornamento sui programmi, che prevedono nuove fasi di progettazione, implementazione
e test. Tali interventi correttivi sono raggruppabili in due distinte famiglie:
1. Manutenzione ordinaria, l’insieme di interventi correttivi necessari per via di errori
sfuggiti ai test o dovuti al funzionamento del programma in condizioni non previste
durante la sua progettazione, come ad esempio il funzionamento su Windows8 di un
programma nato per WindowsXP;
2. Manutenzione evolutiva, l’insieme di interventi di variazione od arricchimento
delle funzioni del programma per via di nuove necessità operative del programma
stesso; un esempio è l’aggiornamento continuo dei programmi gestionali per stare
aggiornati rispetto alle normative fiscali.
In generale, comunque, ogni programma durante la sua vita è soggetto a interventi evolutivi
e correttivi. Solo una piccola percentuale di programmi non viene più toccata dopo il
rilascio.
Le esigenze dei sistemi odierni
A parte alcune eccezioni presenti in determinati settori (“di nicchia”) ancora basati su
sistemi legacy, la stragrande maggioranza dei sistemi informatici, prodotti software inclusi,
è in evoluzione rapida e continua. Il time-to-market, ovvero il lasso di tempo che trascorre
tra l’inizio di un progetto, o l’idea stessa di progetto, ed il suo rilascio sul mercato, è di
importanza strategica per i committenti e per gli sponsor. Che vedono così ripagati in un
tempo ragionevole gli investimenti sostenuti per lo sviluppo del progetto e, qualora riescano
ad essere tra i primi a proporre un prodotto software sul mercato, guadagnano un discreto
vantaggio sui propri concorrenti. Senza dimenticare che la rapidità con cui un prodotto è
disponibile agli utenti finali, soprattutto per i prodotti b2c disponibili on line
immediatamente, e la possibilità di rilasciare on line frequentemente aggiornamenti e
correzioni di eventuali malfunzionamenti, costituiscono un ulteriore incentivo a tenere un
time-to-market il più corto possibile.
12
2. Il ciclo di vita del software
Nell’era del web è inoltre possibile avere feedback sul prodotto a stretto giro, questo
permette a chi gestisce il progetto di decidere se procedere con sviluppi ulteriori sullo stesso
ed eventualmente quante risorse reinvestire in esso.
Inoltre, vista l’elevata dinamicità del sistema, il processo di sviluppo deve essere reattivo
rapidamente ai cambi di idea (mind-change) da parte del committente, in corso d’opera.
Alla luce di tutto questo, il ciclo di vita del software deve essere stretto e condurre ad un
risultato, nel più breve tempo possibile. Risultato che può anche essere composto da un
prodotto minimo che verrà poi ampliato con ulteriori sviluppi. In questi casi si parla di
Minimum Viable Product.
I limiti dei processi di sviluppo tradizionali
“Se l’industria dei trasporti avesse avuto la stessa evoluzione della tecnologia informatica,
oggi sarebbe possibile volare in 5 secondi da Roma a Stoccolma, con 50 centesimi”
E’ un paragone che descrive perfettamente l’evoluzione dei sistemi informatici hardware e
software.
Risulta perlomeno difficile pensare che modelli sviluppati negli anni 70, siano i più efficaci
da adottare, a distanza di oltre 40 anni, su sistemi e tecnologie completamente diversi nati
durante un progresso tecnologico senza precedenti. Rispetto alle esigenze dei moderni
sistemi, questi modelli presentano pertanto dei limiti, che andiamo brevemente ad
analizzare.
Fase di test tardiva
I test sono una fase cruciale all’interno del ciclo di vita del software. La rapidità con cui è
possibile rilasciare correzioni di bug o malfunzionamenti, non deve in nessun modo essere
un incentivo a rilasciare software con difetti più o meno evidenti, nonostante questi siano
purtroppo inevitabili.
Introdurre i test solamente all’ultimo momento, appena prima della messa in produzione,
dopo che il progetto è stato di fatto completato e migliaia di righe di codice sono state scritte,
può essere rischioso. Devono essere individuati con precisione tutti gli scenari da testare,
con tutti i possibili casi d’uso e, in questo, un’analisi completa e ben fatta è sicuramente di
aiuto. I problemi possono però insorgere nel momento in cui dovessero emergere
malfunzionamenti. Questi vanno riportati agli sviluppatori i quali potrebbero avere difficoltà
ad individuarne le cause in mezzo a tutto il codice già scritto che costituisce il progetto
finendo inevitabilmente a ritardare il completamento del progetto.
Burocrazia
La specifica dei requisiti produce un documento scritto che vincola il prodotto da sviluppare
e ciò non sempre soddisfa le esigenze del cliente. Si tratta pur sempre di specifiche basate
13
2. Il ciclo di vita del software
su un documento inanimato che non necessariamente aiuta nel definire le esigenze, che,
invece, appaiono subito chiare dopo il primo rilascio del software, inoltre tale documento
deve essere completo e chiaro prima di procedere allo sviluppo, ma non sempre ciò è
possibile.
L’utente spesso non conosce tutti i requisiti dell’applicazione perché non può conoscerli,
motivo per cui non sempre il documento dei requisiti è completo e, quindi, si ha un passaggio
alla fase successiva con documentazione incompleta o poco chiara.
Il modello a cascata, e le sue evoluzioni, obbligano a usare standard pesantemente basati
sulla produzione di una data documentazione in determinati momenti per cui il lavoro rischia
di essere burocratizzato.
Attività svolte controvoglia e/o sotto scadenza
A meno che in azienda non siano presenti figure preposte, cosa assai rara, le attività
burocratiche, test e scrittura documentazione su tutte, vengono svolte dagli sviluppatori.
Uno dei pilastri del modello a cascata è proprio la predisposizione della documentazione
funzionale del prodotto.
Salvo rari casi, nessuno sviluppatore (per lo meno nessuno tra le centinaia di colleghi
incontrati in quasi 13 anni di carriera, ndA) svolge volentieri queste attività, spesso viste
come un fastidio (“Il mio lavoro è scrivere codice”) e quindi portate avanti, più o meno
inconsciamente, controvoglia. Nel caso test e documentazione siano da svolgere sotto
scadenza a causa di ritardi nella fase di scrittura codice ed integrazione dei sorgenti, la
situazione non può che peggiorare.
Le metodologie Agili (si veda il prossimo capitolo) mettono a disposizione una tecnica
molto potente per oviare, almeno in parte, al “peso” dei test (TDD, Test Driven
Development) e suggeriscono di adottare uno stile di scrittura del codice pulito e lineare
“che si documenti da solo”. Esistono inoltre tool e framework che facilitano il lavoro di
scrittura della documentazione, anche di quella meno tecnica (BDD, ad esempio).
Regressione
I test vengono spesso svolti manualmente dagli sviluppatori o da personale addetto (Tester).
Nel caso la funzionalità sviluppata sia completamente nuova, si può essere ragionevolmente
certi che una volta superati i test previsti, il rischio di rilasciare bug o malfunzionamenti in
produzione è relativamente contenuto, a patto che il documento di test (Test case) preveda
adeguata copertura.
Nel caso invece il rilascio riguardi una modifica ad una funzionalità già rilasciata in
precedenza, potrebbero presentarsi problemi. Vi è infatti il rischio concreto che queste
modifiche vadano ad introdurre bug indesiderati in parti del prodotto interessate
indirettamente dalla modifica, introducendo così il fenomeno della regressione. Per
contenere questo rischio dovrebbe essere sempre pianificata una suite di test appositi
(Regression test). Si tratta anche in questo caso di test manuali più difficili da pianificare e
non sempre svolti completamente. Se lo sviluppo coinvolge il rilascio urgente di correzioni
14
2. Il ciclo di vita del software
di bug emersi in produzione la fase di regression test potrebbe venire svolta in maniera più
o meno incompleta, accentuando il rischio di introdurre regressione.
Linearità
Spesso si hanno cicli di feedback per la correzione degli errori. Tale feedback deve essere
lineare e, quindi, non si possono effettuare salti a ritroso ma vanno ripercorse tutte le fasi in
maniera lineare.
Rigidità
Nella metodologia a cascata, ogni fase viene congelata quando si passa alla fase successiva,
per cui non è possibile un’interazione tra clienti e sviluppatori durante il ciclo di vita dopo
la parte iniziale.
Tutta l’analisi è concentrata all’inizio del ciclo di vita. Tutti i possibili scenari d’uso, le
funzionalità e le caratteristiche che il prodotto dovrà avere devono essere chiariti durante
questa fase, prima che vengano definiti i dettagli implementativi e una singola linea di
codice venga scritta. Eventuali dubbi o problematiche che dovessero emergere in una fase
successiva come, ad esempio uno scenario di difficile implementazione oppure modifica
delle specifiche da parte del committente, verrebbero gestite in modo articolato.
Tutto il modello è orientato alla singola data di rilascio (Monoliticità) che spesso si pone a
mesi o anni dopo l’inizio della prima fase per cui se vengono commessi eventuali errori o
cambiano i requisiti, questi verranno implementati dopo parecchio tempo e comunque alla
fase di consegna seguirà subito un altro adattamento perché il software sarà già obsoleto.
Come descritto in precedenza, i sistemi moderni richiedono flessibilità, massima reattività
ai frequenti cambiamenti di specifiche in corsa nonché un processo snello che conduca in
tempi rapidi ad un risultato, anche minimo e/o parziale.
15
2. Il ciclo di vita del software
16
3. Agile Programming
3. Agile Programming
“Se ti andasse a fuoco la casa, spiegheresti ai pompieri cosa devono
fare e come? No, perché li reputi esperti e capaci di auto-organizzarsi.
La stessa cosa vale per un team Agile”
(Coach Agile, corso Volagratis, settembre 2013)
Cosa significa Agile? Perché è vantaggioso?
Con il termine “Metodologie Agili” ci si riferisce ad una serie di metodologie di sviluppo
software ispirate dal “Manifesto Agile”, impiegate per superare i limiti emersi dal modello
tradizionale “a cascata” (waterfall). Tramite queste metodologie i team di sviluppo software
sono in grado di ridurre sensibilmente il rischio di non rispetto dei tempi e/o di commettere
errori di interpretazione dei requisiti, organizzando il proprio lavoro in piccole iterazioni,
chiamate sprint, della durata di poche settimane (tipicamente 2-3) che permettono il rilascio
del software in modo incrementale. In questo modo i progetti software di entità mediogrande possono essere suddivisi in tanti progetti di dimensioni più piccole, aventi ciclo di
vita proprio, rilascio in produzione compreso, lavorabili nell’ambito di un singolo sprint.
Agile non è, in se, un processo caratterizzato da regole. E’ più una filosofia, un “modo di
fare”. Detta linee guida e raccomandazioni che vengono poi implementate da diverse
metodologie. Tra le più comuni citiamo:
Extreme Programming (XP) - Per le attività ingegneristiche di sviluppo software.
Scrum - Per le attività di gestione del processo nel suo insieme.
Tra i principali vantaggi offerti dall’Agile Programming, vi sono una maggiore qualità del
software rilasciato, in quanto ogni parte incrementale di prodotto viene testata nell’ambito
di uno sprint, ed una maggiore capacità di adeguamento del team di sviluppo ad eventuali
variazioni in corso d’opera dei requisiti da parte del committente, cosa molto attuale nei
progetti software odierni.
Uno dei requisiti chiave per il buon funzionamento dell’impiego delle metodologie agili è
la continua interazione tra sviluppatori (in toto o tramite rappresentanti) ed il committente
(cliente o Product Owner) al fine di aver sempre una visione chiara ed aggiornata di
esigenze, requisiti e feedback del lavoro svolto da una parte e dello stato dell’arte del
prodotto dall’altra.
Come riportato sul sito web www.agileprogramming.org [7], l'Agile Programming
offre ai team ripetute possibilità di valutare un progetto all'interno del suo ciclo di vita, inteso
come insieme di sviluppi incrementali, e di aggiornare quindi le valutazioni precedenti in
base a cambiamenti del contesto. Queste possibilità sono diretta conseguenza del sistema di
lavoro a sprint. Al termine di ogni sprint il team presenta un pezzo di software (quindi un
componente software) funzionante al Product Owner per approvazione. Questa enfasi su un
pezzo di software garantisce che il team ha correttamente compreso i requisiti. Dal momento
che gli sprint si ripetono ed il prodotto continua a guadagnare funzionalità (incrementi di
prodotto), l'agile programming è descritto come "iterativo" e "incrementale". Nel modello
tradizionale o a cascata, il team ha una sola occasione per svolgere correttamente ogni parte
17
3. Agile Programming
di un progetto. Non è così nell'agile programming, dove ogni aspetto relativo allo sviluppo
è revisionato durante l'intero ciclo di vita di un progetto. Virtualmente, non c'è possibilità
che un progetto segua a lungo una direzione errata. Dal momento che il team verifica con il
committente lo stato del progetto (ed i rilasci incrementali) cadenze regolari, c'è sempre il
tempo per cambiare in corsa (strategia "inspect-and-adapt").
Gli effetti di questa strategia "inspect-and-adapt" sono evidenti. Riducono
significativamente i costi di sviluppo ed il time to market. I team Agili proseguono e
raffinano la raccolta ed analisi requisiti durante lo sviluppo del prodotto, e questo non può
quindi ostacolare il progresso del lavoro svolto dal team. Inoltre dal momento che il team
sviluppa in cicli di lavoro corti e ripetitivi, gli Stakeholders hanno l'opportunità di assicurarsi
che il prodotto in sviluppo incontri la vision del committente.
In sostanza, si può affermare che l'Agile Programming aiuta le aziende a costruire il prodotto
che i clienti desiderano. Anziché consegnare software "fumoso" e sub-ottimizzato, l'Agile
Programming mette in condizione i team di costruire il miglior software possibile. L'Agile
Programming protegge quindi l'adeguatezza del prodotto rispetto al mercato e previene il
rischio che il lavoro svolto finisca in un cassetto senza mai venir rilasciato e questo
"accontenta" sia sviluppatori che Stakeholders.
Ruoli Agili
Introduciamo alcuni ruoli coinvolti in un’organizzazione Agile. Essendo Agile una filosofia,
le caratteristiche specifiche di dettaglio di ogni ruolo dipendono dal processo e dalla
metodologia adottata.
Product Owner: E’ il responsabile dell’attività del team. Accetta o respinge le richieste del
committente e, assieme a lui, le prioritizza. Una volta raccolte le richieste del cliente le
propone al team sotto forma di attività da fare. In funzione della priorità e della capacità di
lavoro del team, queste attività vengono opportunamente inserite negli sprint ai quali il team
lavorerà.
Sviluppatori: Cuore pulsante del team. Analizzano tecnicamente le richieste pervenute dal
committente tramite il Product Owner e ne stimano la portata. Si occupano poi di design,
coding e test del software lavorato durante ogni sprint.
Stakeholders: Tutti coloro che sono coinvolti in un prodotto. Non fanno parte del team.
Oltre agli utenti finali, potrebbero essere stakeholders anche altri membri
dell’organizzazione che devono essere coinvolti. Ad esempio l’ufficio legale nel caso il
software abbia alcuni aspetti legali da gestire (es. trattamento dati personali, oppure
accettazione condizioni di utilizzo) oppure gli amministratori di sistema nel caso il software
debba essere installato su macchine che richiedono particolari accorgimenti hardware e
software.
Business Owners: Uno o più stakeholders. Hanno un ruolo attivo durante il ciclo di
sviluppo. Supportano il team qualora il Product Owner e gli sviluppatori dovessero
richiedere chiarimenti e vengono coinvolti in test del prodotto in lavorazione. Agli occhi
dell’organizzazione sono considerati, al pari del team di sviluppo, responsabili della buona
riuscita del progetto.
18
3. Agile Programming
Agile Coach: E’ un esperto delle metodologie Agili. Il suo compito è quello di supportare
uno o più team nell’adozione e nell’utilizzo di queste metodologie e quindi contribuire ad
una crescita professionale costante dei membri del team.
Agile manifesto e Agile programming
Pubblicato nel 2001, il “Manifesto Agile” (agilemanifesto.org [8]) è il documento in cui
vengono elencati i concetti che ispirano la metodologia Agile. I firmatari, in ordine
alfabetico, sono Kent Beck, Mike Beedle, Arie van Bennekum, Alistair Cockburn, Ward
Cunningham, Martin Fowler, James Grenning, Jim Highsmith, Andrew Hunt, Ron Jeffries,
Jon Kern, Brian Marick, Robert C. Martin, Steve Mellor, Ken Schwaber, Jeff Sutherland,
Dave Thomas.
Di seguito vengono riportate la versione originale tratta da [8] e la traduzione italiana del
manifesto (© 2001 gli autori sopra elencati)
“We are uncovering better ways of developing
software by doing it and helping others do it.
Through this work we have come to value:
Individuals and interactions over processes and tools
Working software over comprehensive documentation
Customer collaboration over contract negotiation
Responding to change over following a plan
That is, while there is value in the items on
the right, we value the items on the left more.”
“Stiamo scoprendo modi migliori di creare software,
sviluppandolo e aiutando gli altri a fare lo stesso.
Grazie a questa attività siamo arrivati a considerare importanti:
Gli individui e le interazioni più che i processi e gli strumenti
Il software funzionante più che la documentazione esaustiva
La collaborazione col cliente più che la negoziazione dei contratti
Rispondere al cambiamento più che seguire un piano
Ovvero, fermo restando il valore delle voci a destra,
consideriamo più importanti le voci a sinistra.”
19
3. Agile Programming
Il manifesto suggerisce linee guida da adottare in un ambito di Agile programming, ed è
stato ispirato da 12 principi così sintetizzati:












La nostra massima priorità è soddisfare il cliente rilasciando software di valore, fin
da subito e in maniera continua.
Accogliamo i cambiamenti nei requisiti, anche a stadi avanzati dello sviluppo. I
processi agili sfruttano il cambiamento a favore del vantaggio competitivo del
cliente.
Consegniamo frequentemente software funzionante, con cadenza variabile da un
paio di settimane a un paio di mesi, preferendo i periodi brevi.
Committenti e sviluppatori devono lavorare insieme quotidianamente per tutta la
durata del progetto.
Fondiamo i progetti su individui motivati. Diamo loro l'ambiente e il supporto di cui
hanno bisogno e confidiamo nella loro capacità di portare il lavoro a termine.
Una conversazione faccia a faccia è il modo più efficiente e più efficace per
comunicare con il team ed all'interno del team.
Il software funzionante è il principale metro di misura di progresso.
I processi agili promuovono uno sviluppo sostenibile. Gli sponsor, gli sviluppatori e
gli utenti dovrebbero essere in grado di mantenere indefinitamente un ritmo costante.
La continua attenzione all'eccellenza tecnica e alla buona progettazione esaltano
l'agilità.
La semplicità - l'arte di massimizzare la quantità di lavoro non svolto - è essenziale.
Le architetture, i requisiti e la progettazione migliori emergono da team che si autoorganizzano.
A intervalli regolari il team riflette su come diventare più efficace, dopodiché regola
e adatta il proprio comportamento di conseguenza.
20
3. Agile Programming
Superare i limiti dello sviluppo tradizionale
Figura 9 Evoluzione a spirale del modello a cascata
Un articolo pubblicato in [9] evidenzia come nel metodo tradizionale a cascata le fasi dello
sviluppo software sono consecutive e sempre nello stesso ordine, ovvero Analisi, Design
(Progettazione tecnica), Coding (Implementazione), Testing. Questo modello è ideale per
progetti:
Contract Based: Il cliente richiede software che rispetti una serie di requisiti messi per
iscritto a priori (esistenza di un contratto). Essendo il modello a cascata "document driven",
porta a contratti basati pesantemente sui requisiti. Questo aiuta a garantire che tutto ciò che
è specificato nel contratto sia rispettato.
Focalizzati sull'analisi: Alcuni sviluppi software richiedono che l'analisi sia totalmente
completata a priori. Questo è il caso di sistemi complessi e/o critici che richiedono diversi
step di validazione ed approvazione. Essendo un processo sequenziale, il modello a cascata
è adatto a questo tipo di progetti
Non richiedono solo il software: ma anche, ad esempio, il manuale dell'utente, lo schema
architetturale, etc. etc. Nello sviluppo tradizionale molti documenti ed artefatti vengono
creati in aggiunta al software stesso e, in alcuni casi, questi prodotti sono considerati
importanti tanto quanto il software.
21
3. Agile Programming
A prescindere dalla filosofia adottata, le principali attività da svolgere non cambiano, quel
che cambia è quando e come vengono svolte. Vediamo uno schema, sempre pubblicato
nell’articolo sopracitato:
Waterfall
Analisi
Architettura e design
Agile
Fatta all’inizio. Tutti i requisiti Tutte le attività sono svolte
vengono identificati a priori
in parallelo durante l’intero
ciclo di vita del progetto
Successivi all’analisi
Pianificazione
Fatta all’inizio del progetto.
Spesso contrattualizzata
Coding
Ha una fase a se. Spesso è pilotato
dai documenti prodotti nelle fasi
precedenti
Testing
Di solito svolto da processi batch
di dimensioni considerevoli
Approvazione/Valutazione Ogni fase (Analisi, Design,
Coding,
Testing)
richiede
approvazione
Lo stesso autore in [10], fa un interessante confronto tra la metodologia classica waterfall
e le metodologie agili, lo riportiamo di seguito.
Nello sviluppo tradizionale, le fasi di Architettura e Design sono considerate le più critiche.
Il team è spinto a creare in anticipo l'architettura che rispetti tutti i requisiti. Questo richiede
molte risorse ed energie all'inizio del progetto.
I principali argomenti sostenuti dai tradizionalisti circa l'importanza di avere l'architettura
definita in toto dall'inizio sono:
-
Il design dell'applicazione non cambia durante lo sviluppo
Aiuta ad evitare di scoprire in una fase successiva che l'architettura non è adatta allo
scopo.
L'Agile, dal canto suo, è basata sull'assunzione che l'incertezza è un fatto comune ed attuale
e così diffusa che nessuna architettura definita rigidamente in principio può, o almeno
potrebbe, essere completamente corretta durante tutto il processo di sviluppo software.
Quindi la filosofia Agile adotta un approccio il più semplice possibile ed incline ai
cambiamenti nei confronti dell'architettura e del design di un progetto.
Dal punto di vista dell'integrazione dei componenti dell'applicazione, l'Agile programming
pone il focus sul fatto che questa avvenga al più presto (incoraggia al rilascio di funzionalità
incrementali al termine di ogni sprint) e che vengano rilasciate funzionalità anziché moduli.
22
3. Agile Programming
Il focus sull'integrazione è ritenuto molto importante in quanto spesso rappresenta una delle
parti più complesse durante la costruzione di un'applicazione software.
Dall'altro lato, l'approccio waterfall è focalizzato sul completamento di moduli tecnici
evidenziati dalla fase iniziale di Architettura e Design. Questo spesso causa parecchi
problemi e deviazioni sulla tabella di marcia, a causa dei problemi che tipicamente sorgono
durante l'integrazione tra loro dei componenti di un'applicazione.
Filosoficamente, l'Agile promuove molti principi volti ad evitare gli sprechi, ad esempio
incoraggiando a tenere la minima documentazione indispensabile, favorendo invece
l'adozione di potenti pratiche ingegneristiche (TDD, pair programming, refactoring, …),
open door management e team auto-organizzati. Questo deriva dal principio che il punto di
forza dello sviluppo sotware sono le persone.
Il metodo tradizionale invece è più orientato ai processi ed alla documentazione. Non pone
nelle persone la stessa importanza/fiducia delle filosofie agili, suggerisce invece misure atte
a misurarne il rendimento e controllarne l'operato.
In definitiva, queste sono le principali differenze tra sviluppo Tradizionale e Agile
Programming
Agile
Waterfall
Architettura infomale ed incrementale
Architettura molto documentata e completata
prima dell’inizio del coding
La ownership del codice è condivisa tra gli Ogni sviluppatore è responsabile di una
sviluppatori
determinata area
Integrazione continua
Integrazione fatta
predeterminate
alla
fine
o
a
tappe
Focalizzato sul completamento delle storie Focalizzato sul completamento di moduli (parti
(funzionalità) in piccole iterazioni (sprint)
dell’architettura) a scadenze prefissate
Ben ingegnerizzato (TDD, XP, design Non necessariamente ingegnerizzato
patterns,...)
Pochi processi e documentazione
Molti processi e documentazione
Sviluppatori cross-competenti, ben informati Pochi architetti/sviluppatori hanno la visione di
su tutte le tecnologie impiegate.
insieme, le altre figure professionali sono molto
specializzate
Ruolo principale: sviluppatori
Ruoli principali: Architetti e sviluppatori
Open door policy: gli sviluppatori sono Solo pochi sviluppatori ed alcuni architetti
incoraggiati a rivolgersi al business ed al possono rivolgersi al business. E principalmente
23
3. Agile Programming
management in qualsiasi momento. Il punto prima dell’inizio dello sviluppo e/o alle scadenze
di vista di tutti deve essere considerato.
prefissate (milestones)
L’Extreme Programming
L’Extreme Programming (XP) è una metodologia di sviluppo adottata per mettere in pratica
le filosofie Agili durante lo sviluppo di uno o più processi software. Tramite questa
metodologia i team di sviluppo sono in grado di rilasciare il software richiesto in tempi
rapidi, così come è stato richiesto e mette in condizione gli sviluppatori di rispondere in
maniera tempestiva alle mutate esigenze del committente o del Product Owner, aspetto
molto importante.
L’extreme programming enfatizza il lavoro di team: manager, clienti e sviluppatori sono
tutti parte di un team collaborativo, in grado di auto-organizzarsi in modo che il problema
(lo sviluppo di un progetto software in questo caso) venga risolto nel metodo più efficiente
possibile.
Un aspetto sorprendente dell’XP sono le sue semplici regole, dal significato trascurabile, se
prese singolarmente, di grande efficacia se applicate, anche solo in parte, assieme.
Di seguito una breve descrizione, tratta da extremeprogramming.org [11] delle
regole e delle pratiche adottate, ed una serie di accorgimenti utili, suddivisi per fase di
progetto:
Pianificazione (Planning Game)
Il Planning Game è una riunione tenuta tipicamente una volta a settimana, ed è composta
da due fasi.
La fase di Release Planning ha lo scopo di determinare, coinvolgendo il cliente, quali
requisiti devono essere lavorati nelle le prossime tre/quattro iterazioni e quando il software
che li soddisfa dovrebbe essere rilasciato. I requisiti vengono tracciati sotto forma di User
Stories per le quali gli sviluppatori forniranno, una volta appresi i requisiti, una stima,
espressa in story-points della mole di lavoro necessaria per poterla rilasciare. Una volta che
la storia è stata stimata, sviluppatori e cliente definiscono in quale sprint sarà lavorata e, di
conseguenza, quando il lavoro verrà consegnato. In questa fase gli sviluppatori devono
tenere conto della capacità di lavoro a loro disposizione ed il cliente sarà quindi chiamato a
fare delle scelte assegnando una priorità alle sue richieste.
24
3. Agile Programming
Figura 10 Le “Planning Poker Cards”. Il Planning Game è, appunto, un “gioco”. Ogni membro del team
può usare una carta per esprimere la sua stima.
La seconda fase invece è quella nominata Iteration Planning, durante la quale il cliente
non è coinvolto. In questa fase gli sviluppatori pianificano la propria attività da svolgere
sulle storie definite durante la fase precedente. Durante questa fase le User Stories possono
venire suddivise in task tecnici. Gli sviluppatori (o le coppie di sviluppatori) prendono in
carico i task ai quali intendono lavorare nell’ambito dell’iterazione.
Gestione
Accorgimenti gestionali utili, volti a mettere il team in condizione di lavorare al meglio:

Sistemare il team in un unico open space.

Impostare un ritmo sostenibile. La capacità di lavoro del team è limitata. E’ bene
che sia chiaro cosa il team è in grado di fare durante uno sprint e quali attività invece
devono essere suddivise su più sprint. Non ha senso, se non in casi di emergenza,
fare affidamento su straordinari ed eroismi individuali per terminare in tempo quanto
pianificato.

Stand up meeting. 10’, in piedi, all’inizio di ogni giorno lavorativo per condividere
le attività svolte e quel che si prevede di fare in giornata.

Misurare la Project Velocity. La Velocity indica quanto lavoro stà venendo
effettivamente fatto nell’ambito del progetto. Un metodo per misurarla consiste nel
confrontare quanti story-points sono stati evasi (storie completate) con quanti ne
sarebbero stati evasi con un andamento regolare. Esempio: in uno sprint da 40 story
points della durata di 20 giorni lavorativi ci si aspetterebbe che al 14° giorno il
numero di story-points evasi non si discosti troppo da 28. Nel caso vi sia uno
scostamento è necessaria una analisi delle cause (stime errate? Urgenze?)
25
3. Agile Programming

Spostare la gente. Tutti devono essere in grado di lavorare ad ogni aspetto del
progetto, in modo da evitare colli di bottiglia o squilibri del carico di lavoro
all’interno del team.

Cambiare quel che non funziona. Nel puro spirito auto-organizzativo del team,
ogni cosa che “non va” deve essere affrontata e risolta assieme.
Design

Semplicità. Non andare a ricercare sempre soluzioni sofisticate, e non reinventare
sempre la ruota.

Impiegare Metafore di sistema. Ovvero assegnare nomi consistenti e facilmente
intuitivi a tutti gli oggetti coinvolti, in modo che siano facilmente spiegabili e
comprensibili.

CRC (Class Responsibility Collaboration) cards game durante le sessioni di
design. Si impiega un foglietto di carta per ogni oggetto (classe) ipotizzato. Il foglio
riporta nome, responsabilità e collaboratori della classe. In questo modo il team
definisce “su carta” (è proprio il caso di dirlo) l’architettura di un insieme di classi
deputate alla soluzione di un problema. Tutto il team è coinvolto e tutti hanno una
chiara visione dell’architettura prodotta.

Creare soluzioni spike per ridurre i rischi. In caso di problemi a soluzione incerta o
fumosa, il team deputa una o due persone allo studio del problema e di una o più
possibili soluzioni, per un periodo predefinito di tempo, in modo che il problema
possa essere affrontato meglio in un prossimo sprint.

Non introdurre funzionalità in anticipo. Le funzionalità rilasciate devono essere
quelle richieste, non quelle che “potrebbero servire in futuro”. Se serviranno,
verranno richieste.

Refactor ovunque, quando possibile. Riscrivere parti di codice di difficile
comprensione (“marcite”) in modo da renderne più semplice la leggibilità e la
manutenibilità.
Coding

Cliente sempre disponibile. In XP il cliente è inteso come un rappresentante
dell’utenza finale. E’ fondamentale che ogni membro del team possa chiarire al più
presto direttamente con lui eventuali dubbi che dovessero insorgere. Chi sia questo
interlocutore deve essere chiaro a tutti i membri del team.

Il codice deve essere scritto seguendo standard concordati (code-conventions).

Test Driven Development (TDD). Il codice viene scritto a partire dai test. Vista
l’importanza e soprattutto l’efficacia della pratica, verrà trattata in un paragrafo
apposta.
26
3. Agile Programming

Pair Programming. Il codice viene scritto da una coppia di sviluppatori usufruendo
di una singola workstation. Uno sviluppatore utilizza tastiera e mouse ed è
focalizzato sul codice in scrittura. L’altro segue più la visione di insieme ed opera
una continua review del codice scritto dal collega. I ruoli vengono invertiti dopo non
più di un’ora, l’ideale è introdurre una pausa durante lo scambio di ruoli.

Commit (integrazioni) frequenti. Ideale 2 volte al giorno.

Code Review. Il codice committato nel sistema di controllo versione (VCS) è frutto
di revisione da parte di altri membri del team, che possono fornire utili suggerimenti
ed indicazioni circa quanto scritto tramite commenti nel codice che verranno recepiti
o meno dall’autore del pezzo interessato.

Impiegare un computer dedicato all’integrazione. Esistono numerosi tools
(Jenkins, ad esempio) che permettono di avere sempre sotto controllo la
compilazione ed il processo di build e packaging della code base integrata.

Collective ownership. “Il codice è di tutti”, tutti devono essere liberi di modificarlo
allo scopo di refattorizzarlo, fixare bug, etc. senza chiedere il permesso all’autore
originale. Questo contribuisce alla diffusione della conoscenza e quindi alla
riduzione dei colli di bottiglia.
Testing

Tutto il codice deve essere coperto da classi di Test Unitari (Unit Test). I test unitari
coprono singole classi o gruppi ristretti di classi a forte collaborazione e vengono
eseguiti durante la build del codice.

Il codice deve superare i test unitari prima che possa essere rilasciato. Il team deve
assicurare sempre il corretto funzionamento dei test unitari. La manutenzione dei
test deve essere importante tanto quanto quella del codice dell’applicazione.

Quando viene rilevato un bug, vanno creati i test che assicurino che non si ripresenti
in futuro.

I test di accettazione (Acceptance tests) vanno eseguiti spesso ed il loro risultato
viene pubblicato. Questa tipologia di test sono test a scatola chiusa, scritti sulla base
delle User Stories e verificano che il sistema dia il risultato atteso.
User Stories
I requisiti del cliente o del Product Owner vengono convertiti, durante il Planning, in User
Stories. Una User Story rappresenta un prodotto (anche parziale) che verrà consegnato al
termine dello sprint. Può essere una storia a se stante, nel caso la richiesta sia semplice,
come ad esempio la raccolta di una informazione supplementare tramite web service.
Nell’ottica dei rilasci incrementali che stà alla base dell’XP, una storia può però anche essere
parte di un progetto più ampio che è stato quindi suddiviso in più storie che non
necessariamente verranno completate tutte nell’arco di un solo sprint. Ad esempio un
27
3. Agile Programming
progetto che prevede una nuova integrazione web services con servizi esterni
all’organizzazione aziendale potrebbe esser diviso in storie del tipo: stabilire la connessione
col servizio esterno, generare gli stub per l’interazione col web service, implementare la
chiamata ad un determinato servizio, e così via.
Le storie vengono scritte in linguaggio semplice e comprensibile, spesso riccorrendo al
paradigma: “In quanto [tipologia di utente] voglio [requisito della storia] in modo da
[scopo del requisito]”. Esempi di User Stories potrebbero essere: “In quanto responsabile
della user-experience voglio che il pulsante “contattaci” presente nella home page del
nostro sito venga posizionato in alto a destra, in modo da renderlo più visibile all’utente
finale” oppure “In quanto applicazione di comparazione prezzi, voglio che venga chiamato
il servizio “catalogo” dei web services esposti dal fornitore XXX, in modo da poter avere
sulla mia applicazione i loro articoli ed i relativi prezzi”.
Le User Stories vengono accompagnate da criteri di accettazione (acceptance criteria). Una
serie di test black box, definiti appunto acceptance tests, che verificano l’effettivo rispetto
dei requisiti da parte della storia. Questi test vengono eseguiti solitamente a mano da persone
diverse rispetto allo sviluppatore o agli sviluppatori che hanno lavorato alla storia
(potrebbero essere anche il cliente o il Product Owner) prima che questa venga rilasciata in
produzione, allo scopo di “certificare” il lavoro fatto dagli sviluppatori.
L’entità di una User Story deve essere sempre stimabile in fase di planning. Nel caso questo
non sia possibile, sono necessarie ulteriori iterazioni con Product Owner e/o cliente al fine
di raccogliere gli elementi necessari alla stima. Nel caso questo non sia possibile, si ricorre
a spike. Per spike si intende un task che non può esser considerato una User Story in quanto
non ne sono chiari requisiti ed entità, ma è comunque necessario che uno o più sviluppatori
investano tempo a definire meglio l’attività richiesta affinché possa diventare una User
Story.
Test Driven Development (TDD)
Spesso la fase di testing è vista come un fastidio da parte degli sviluppatori software, ed
ancor di più lo è la necessità di dover scrivere delle classi di test (codice che testa il codice).
La tecnica TDD consiste nella stesura del codice a partire dalle classi di test che ne
assicurano il buon funzionamento, test unitari (Unit Test) in questo caso. Questo potente
approccio garantisce la copertura di test quasi totale (test coverage) del codice scritto e
riduce significativamente il rischio che nel codice possano esservi bug.
Una volta individuati uno o più scenari riguardanti il funzionamento ed il comportamento
del software che si va a scrivere, questi vengono convertiti in test unitari. Utile in questi casi
è il paradigma “Given-When-Then”. Ad esempio “Dato che (Given) 4 diviso 2 risulta 2,
Quando (When) chiamo il metodo dividi, Allora (Then) il metodo restituisce 2” ma anche
“Dato che 0 non è un divisore valido, quando chiamo il metodo dividi, allora il metodo mi
restituisce un errore”.
28
3. Agile Programming
Una volta individuati tutti i casi di test si procede secondo questo ciclo, introducendo un
caso di test alla volta:
1. Si scrive il codice relativo al caso di test
2. Lo si lancia e lo si lascia fallire
3. Si scrive codice con il solo scopo di far avere esito positivo a tutti i test scritti
(compreso quello fallito al punto 2)
4. Si procede al refactoring del codice scritto al fine di eliminare gli smell. Gli smell
sono porzioni di codice che potrebbero, col tempo, portarlo a degenerare (esempio
metodi lunghi o ripetuti, ricorso a variabili inutili, …).
Un ulteriore beneficio dato da questa procedura è che il codice scritto, essendo scritto in
modo incrementale e soggetto a continui refactoring rimane semplice e facilmente
comprensibile e manutenibile.
Valori ispiratori dell’XP
L’Extreme Programming è basato su valori. Le regole sopraelencate altro non sono che la
naturale estensione e la conseguenza della massimizzazione di questi valori. Infatti, l’XP
non è in realtà un insieme di regole ferree, quanto un modo di lavorare in armonia con i
valori personali ed aziendali. I valori ispiratori dell’XP, così come sono definiti in [12] sono
di seguito descritti.
“Semplicità: Faremo quanto necessario e richiesto, nulla di più. Questo massimizzerà il
valore creato a fronte dell’investimento fatto. Faremo piccoli e semplici passi verso
l’obiettivo e miticheremo i fallimenti quando accadono. Creeremo qualcosa di cui saremo
orgogliosi e lo manterremo a lungo a costi ragionevoli
Comunicazione: Siamo tutti parte di un team e comunichiamo faccia a faccia
quotidianamente. Lavoreremo assieme su tutto, dai requisiti al codice. Creeremo assieme
la miglior soluzione possibile al problema.
Feedback: Prenderemo seriamente ogni commitment ad ogni iterazione, rilasciando
software funzionante. Mostriamo spesso il lavoro fatto (demo), quindi prestateci attenzione
e richiedete ogni modifica ritenuta opportuna. Parleremo del progetto ed adatteremo ad
esso il nostro processo e non il contrario.
Rispetto: Ognuno dà e riceve il rispetto che si merita in quanto membro del team. Ognuno
apporta valore anche quando questo è semplicemente entusiasmo. Gli sviluppatori
rispettano l’esperienza dei clienti, e vice versa. Il Management rispetta il nostro diritto di
accettare responsabilità e ricevere autorevolezza circa il nostro stesso lavoro.
Coraggio: Diremo la verità circa progressi e stime. Non documentiamo scuse per il
fallimento perché ci aspettiamo di riuscire nel nostro scopo. Non abbiamo paura di nulla
perché nessuno lavora da solo. Ci adatteremo ai cambiamenti ogni volta che dovessero
accadere.”
29
3. Agile Programming
Un caso di successo: SCRUM
Lo Scrum è una metodologia Agile iterativa ed incrementale per la gestione dello sviluppo
di prodotti software. E' definita come "una strategia flessibile ed olistica di sviluppo, dove
il team lavora unito al raggiungimento di un obiettivo comune". Il termine Scrum è mutuato
dal rugby, infatti, indica il pacchetto di mischia. Come metodologia Agile, mette in
discussione i principi dell'approccio tradizionale ed incoraggia la auto-organizzazione dei
team e la comunicazione continua (fisica o virtuale) tra membri del team.
Un principio chiave dello Scrum è il riconoscimento che, durante un progetto, il committente
può cambiare idea su requisiti e necessità, e che questi cambi di idea non possono essere
gestiti facilmente tramite un progetto gestito con approccio tradizionale.
Scrum pone l'accento sull'abilità del team a rilasciare in maniera rapida e rispondere
altrettanto rapidamente ai cambiamenti emergenti anche eventualmente a discapito di una
comprensione e/o definizione solo parziali del problema nella fase iniziale (fonte: [13])
Figura 11 Un team Scrum davanti alla propria “board”
Nata come metodologia di sviluppo software, è stata adottata da diverse multinazionali (es.
Toyota) per la gestione dei processi in altre aree aziendali.
30
3. Agile Programming
Ruoli
Un team Scrum è composto dai già descritti Product Owner e Developers, dallo Scrum
Master, il cui compito è quello di agevolare il lavoro del team, collaborando col Product
Owner alla definizione del progetto affinchè i requisiti siano chiari, assicurandosi che il team
sia in grado di auto-organizzarsi secondo i principi Scrum (in questo caso agisce com Agile
Coach) e facendosi carico di tutte le incombenze che potrebbero distrarre gli sviluppatori
dalla loro attività primaria. Lo Scrum Master, ad esempio, si rivolge ai sysadmins in caso di
necessità tecniche (acquisto e manutenzione workstations, richiesta server di sviluppo,
collaudo e/o produzione) oppure alle funzioni preposte in caso di necessità logistiche
(scrivanie, stanze, materiale di cancelleria, …).
Metodologia di lavoro
Di seguito si riporta un interessante schema, tratto da [14] che riassume la metodologia di
lavoro Scrum.
31
3. Agile Programming
Backlog
Il backlog è un elenco di User Stories modellate dal Product Owner assieme al committente
ed eventualmente uno o più sviluppatori, sulla base di quelli che sono i requisiti del cliente.
Le User Stories possono anche venir modellate in maniera “approssimativa” e raffinate
successivamente.
Esistono tre tipi di backlog, di prodotto, di team e di sprint. Il backlog di prodotto
raccoglie i requisiti divisi per tipologia di prodotto, e quindi per i relativi Stakeholders e
Business Owners. Queste storie vengono poi smistate dai Product Owner dei vari team
all’interno del backlog di team del team che se le prenderà in carico. Una volta che sono in
questo backlog, il team le può analizzare e stimare (grooming) ed includerle nel backlog di
sprint relativo allo sprint nel quale il team le lavorerà.
Figura 12 Esempio di backlog di sprint, sono indicati gli estremi dello sprint e (alcune) storie in
lavorazione.
Grooming
Dal momento che la metodologia Scrum incentiva la definizione “parziale” delle User
Stories nella loro fase iniziale, il team è chiamato a fare attività di Backlog Refinement una
volta che queste sono state prese in carico dal Product Owner ed inserite nel backlog di team.
Questa attività viene svolta tramite riunioni periodiche di durata variabile definite sessioni
di grooming (letteralmente “toelettatura”). Durante queste sessioni il team analizza
(“spulcia”) i requisiti del cliente e, se questi sono abbastanza chiari, procede alla definizione
dei dettagli tecnico/implementativi della storia ed alla eventuale sua suddivisione in più task
32
3. Agile Programming
tecnici da svolgere, viceversa vengono contattati gli stakeholders della storia per ulteriori
chiarimenti.
Dopo aver definito i dettagli della storia, il team procede a stimarne l’entità. Unità di misura
tipica dell’entità di una storia sono gli story points. La sequenza più comune adottata per
definire gli story points è la serie di Fibonacci. Nello spirito di auto-organizzazione del team,
la metodologia Scrum sconsiglia di paragonare il concetto di story point ad altre misure
(giorni/uomo in primis). L’unico modo per misurare uno story point è paragonare tra loro le
storie e le relative stime fornite dal team. Secondo questo principio, il concetto di story point
può variare da team a team.
Planning e Scrum Board
Prima dell’inizio di ogni sprint il team si riunisce in una sessione di planning. Durante
questa riunione, lo Scrum Master calcola il numero di story points disponibili durante lo
sprint in partenza, in funzione di durata dello sprint, presenze degli sviluppatori ed
andamento degli sprint precedenti (rapporto tra story points pianificati e story points
effettivamente evasi).
E’ buona norma tenere conto anche del tempo investito dal team su interferenze esterne a
cui può essere soggetto (imprevisti, urgenze, ...). In funzione degli story points disponibili,
il Product Owner propone al team un elenco di storie da mandare in lavorazione, prese dal
backlog di team tra quelle già analizzate e stimate durante un grooming.
Gli sviluppatori accettano le proposte del Product Owner, oppure propongono modifiche
circa le storie da lavorare. Il Product Owner, accetta o respinge le proposte degli sviluppatori
in funzione delle priorità delle storie per i relativi Business Owner. Le storie selezionate per
lo sprint, entrano quindi a far parte del backlog di sprint.
33
3. Agile Programming
Figura 13 Una Scrum Board alla quale sono appese le storie appartenenti allo sprint (colonne) e il loro
stato (righe).
Uno dei punti della metodologia Scrum, afferma che tutto ciò che è in lavorazione all’interno
di un team deve essere “visualizzato”. Nell’ambiente di lavoro di un team Scrum “i muri
parlano”. Sono presenti infatti grafici, poster, post-it e qualunque strumento possa
contribuire alla “visualizzazione” del lavoro da parte del team.
Uno strumento sempre presente è la “Scrum Board”, detta anche “Scrum Wall”. Si tratta di
una tabella a doppia entrata nella quale da un lato vengono inserite le card relative alle storie
in lavorazione durante lo sprint, ed i relativi task e dall’altro lo stato di lavorazione del task
o della storia (ad esempio: “Da fare”, “In lavorazione”, “Da revisionare”, “In test”, “Fatto”).
L’organizzazione degli stati da inserire nella Scrum Board, così come le regole per il
passaggio da uno stato all’altro sono lasciate all’auto-organizzazione del team. In particolare
il team definisce un set di regole (“Definition of Done”, DoD) che disciplinano la messa in
“Fatto” di un task o di una storia.
34
3. Agile Programming
Figura 14 Esempio di User Story così come appare sulla Scrum Board
Figura 15 Descrizione e criteri di accettazione della storia di cui sopra
Sprint
Il lavoro di uno Scrum team viene scandito dagli sprint. Durante gli sprint gli sviluppatori,
soli o in pair lavorano alle User Stories pianificate durante la sessione di planning.
Quotidianamente il team si riunisce in stand-up meetings, incontri tenuti davanti alla
Scrum-Board della durata massima di 10’ - 15’ (e proprio per questo svolti in piedi, standup). Durante questi incontri gli sviluppatori condividono tra loro l’attività svolta il giorno
precedente ed i programmi per la giornata in corso. Eventuali spunti che richiedono
discussioni più approfondite (es. dubbi su dettagli tecnici da adottare) che dovessero
emergere durante lo stand-up vengono rimandati ad incontri successivi senza che sia
necessaria la partecipazione dell’intero team.
35
3. Agile Programming
Figura 16 Stand up meeting in corso
Al termine dello stand-up, viene aggiornata la Burndown Chart, un grafico giorni/storypoints, indicando il numero di story points ancora da evadere alla data dello stand-up. Sulla
Burndown Chart viene anche riportato l’andamento ideale di uno sprint (stesso numero di
story points evasi tutti i giorni).
L’analisi degli scostamenti tra andamento ideale e quello reale è un indicatore sia delle
prestazioni del team che, soprattutto, della correttezza delle stime fatte durante le sessioni
di grooming. Un andamento sotto la media, più veloce di quello ideale potrebbe indicare
storie sovrastimate. Parimenti, un andamento troppo sopra la media, più lento rispetto
all’ideale, indica uno sprint problematico, nel corso del quale le storie sono state
sottostimate, oppure la presenza di situazioni particolari (es. urgenze) che hanno inciso sul
tempo a disposizione per la lavorazione delle storie.
Figura 17 Esempi di burndown chart sull’asse delle ascisse vi sono i giorni sprint, sulle ordinate gli
Story Points residui
36
3. Agile Programming
Demo
Nei giorni immediatamente precedenti o successivi il termine dello sprint, il team presenta
al Product Owner ed agli Stakeholders il lavoro fatto e prossimo al rilascio in produzione,
organizzando una sessione di Demo. In questo modo gli Stakeholders hanno, tra le altre
cose, evidenza del fatto che gli investimenti fatti nelle ultime settimane (lavoro degli
sviluppatori e costi accessori) saranno a brevissimo ripagati con il valore del prodotto creato
dal team (ROI immediato o quasi).
Retrospettiva
Terminato lo sprint, il team si riunisce in retrospettiva. Un meeting dedicato
esclusivamente al team, senza interferenze esterne (se non concordate e motivate). Durante
questo incontro, viene discusso l’andamento dello sprint, le cose andate bene, quelle andate
male e quindi migliorabili. Vengono decise azioni da intraprendere negli sprint successivi
in modo che il team possa lavorare in condizioni migliori rispetto a quello precedente e tutti
siano in grado di rendere al meglio.
E il giro ricomincia...
37
3. Agile Programming
38
4. Continuous Development
4. Continuous Development
You can't just ask customers what they want and then try to give that to them.
By the time you get it built, they'll want something new.
[Steve Jobs]
Il processo di Continuous Development
Con il termine Continuous Development ci si riferisce ad una serie di tecniche che
permettono lo sviluppo iterativo di applicazioni software. Le funzionalità sviluppate, così
come le modifiche, diventano immediatamente parte di un prodotto software. Senza bisogno
di attendere l’esecuzione di processi di integrazione. A seconda della tecnica adottata,
vedremo che il concetto di “immediatamente disponibile” può variare.
Continuous Integration
La Continuous Integration è una pratica tramite la quale gli sviluppatori committano sul
sistema SCM il proprio lavoro in modo frequente, almeno una volta al giorno. Ogni commit
scatena un processo di compilazione e build della codebase presente nel sistema SCM
(esempio GitHub, Svn, …) che la verifica, eseguendo anche una suite di test automatici
predisposti dagli sviluppatori. Nel caso qualcosa non vada per il verso giusto durante questo
processo di build e test (condizione detta di “build rotta”) gli sviluppatori ricevono notifica
immediata del problema e possono porvi rimedio immediatamente, eventualmente facendo
rollback del commit che ha causato problemi. Uno dei requisiti della pratica CI è che il
codice presente sotto SCM sia sempre compilabile, buildabile e superi i test automatici a cui
è sottoposto.
Questo approccio porta ad una significativa riduzione dei problemi di integrazione e
permette ai team di sviluppare software coeso in maniera rapida.
Si noti come questa pratica sia in netta contrapposizione ai metodi tradizionali, che
prevedono integrazione differita, che avviene solamente a fine sviluppo, dopo un periodo
che può durare settimane (ma anche mesi o addirittura anni!) e si pensi anche a quanto
problematico possa essere gestire problemi di integrazione che dovessero emergere in questa
fase.
Continuous Delivery
La Continuous Delivery è la naturale estensione della Continuous Integration.
Tramite questo approccio, gli sviluppatori garantiscono che ogni modifica apportata al
codice, committata sull’SCM ed integrata (Continuous Integration) è potenzialmente
rilasciabile in produzione tramite un processo automatico avviato con un click (push button
deploy).
Per arrivare ad avere del software rilasciabile è necessario che la codebase presente
sull’SCM sia sempre buildabile. Ricordiamo che la build prevede l’esecuzione sul codice
39
4. Continuous Development
compilato di una serie di test automatici volti a garantire il rispetto dei requisiti da parte del
software. Le pratiche di Continuous Delivery non escludono completamente l’esecuzione di
test manuali che, possono venire eseguiti, per particolari funzionalità critiche non testabili
in modo automatico, oppure per funzionalità nelle quali è necessario un feedback utente,
prima di lanciare il processo di pubblicazione del software in produzione con un semplice
click.
Questa pratica tende a semplificare, in termini di operazioni da compiere, un processo da
sempre critico come il rilascio in produzione del software.
Spesso il rilascio viene eseguito raramente a causa sia della complessità delle operazioni da
fare, sia per la “paura” che qualcosa vada storto durante queste operazioni unita a quella di
rilasciare in produzione software malfunzionante. A questo si aggiunge, talvolta, il fatto che
il rilascio ha una durata importante e viene fatto in orari in cui il sistema è sottoposto a
carichi minori, che spesso coincidono con orari festivi e/o notturni.
Le pratiche di Continuous Integration e Continuous Delivery permettono di superare
agilmente questi limiti introducendo test a copertura del codice prodotto, ed una sequenza
automatizzata delle operazioni di rilascio. Tra le conseguenze dei rilasci frequenti una delle
più importanti è la riduzione della differenza (delta) tra due versioni consecutive e quindi
un rollback non troppo complicato, e al tempo stesso non troppo penalizzante per gli utenti,
che dovranno temporaneamente rinunciare alle nuove funzionalità rilasciate.
Continuous Deployment
Lo step successivo è il Continuous Deployment. Tramite questo processo, viene rimosso
l’intervento umano minimo previsto dalla Continuous Delivery che avvia il rilascio in
produzione del software. Potenzialmente, ogni commit delle modifiche al codice innesca un
processo di compilazione, build e testing automatico del software. Qualora la build del
codice ed i test automatici abbiano esito positivo, si procede alla crezione di eseguibili (war,
exe, jar,...) e, sempre automaticamente, alla pubblicazione in produzione dell’eseguibile
generato.
Nei progetti per i quali vengono adottate la Continuous Delivery ed il Continuous
Deployment, di norma, non si applicano regole di stop allo sviluppo (code freeze).
Trattandosi comunque di metodologie di ispirazione Agile, questo aspetto viene lasciato
all’auto-organizzazione degli sviluppatori.
40
4. Continuous Development
Figura 18 Schema logico che illustra come Continuous
Delivery e Continuous Deployment siano estensioni della
Continuous Integration.
Un concetto fondamentale nel Continuous Deployment è quello di pipeline, un pattern che
copre tutte le fasi della produzione software, dallo sviluppo al rilascio. La pipeline modella
una sequenza di operazioni che vengono svolte automaticamente, dal momento in cui una
modifica software viene committata sull’SCM, a quando questa diviene parte del software
rilasciabile. L’esecuzione della pipeline può partire automaticamente ogni volta che
vengono effettuati commit sulla codebase del progetto, può essere pianificata per girare ad
orari o intervalli predefiniti, oppure può essere lanciata manualmente da personale abilitato
a farlo.
Figura 19 Schema astratto della pipeline.
41
4. Continuous Development
Cosa serve per impostare il processo di Continuous Deploy
Figura 20 Esempio di sequence diagram di una deployment pipeline eseguita automaticamente ad ogni
commit su sistema VCS. Notare l’importanza del feedback ad ogni stato.
Sia Jez Humble e David Farley, nel loro libro “Continuous Delivery” [15], che Martin
Fowler in un interessante articolo pubblicato in [16], elencano e descrivono una serie di
pratiche ottimali da adottare per implementare processi in ambito Continuous Development.
Le pratiche vengono descritte da un punto di vista astratto, senza scendere nel dettaglio
tecnico sul come metterle in pratica. Esistono numerosi tool, sia a pagamento che opensource, che permettono, singolarmente o in uso combinato tra loro, di raggiungere gli scopi
dettati dalle pratiche descritte. Nella sezione relativa all’attività pratica svolta in Volagratis,
vedremo un esempio di come queste pratiche sono state implementate dal punto di vista
tecnico e quali strumenti sono stati utilizzati. Riportiamo le pratiche, estratte dalle
pubblicazioni sopracitate.
Unico repository per i sorgenti
I progetti software (programmi) coinvolgono numerosi file che devono essere
opportunamente combinati tra loro per costruire un prodotto (una vera e propria
42
4. Continuous Development
orchestrazione). Tenerne traccia è un effort oneroso, in particolare quando sono coinvolte
più persone. Non c'è da sorprendersi quindi che, negli anni, i team di sviluppo software
abbiano costruito tools per gestire tutto questo. Questi tools, chiamati Source Code
Management, Configuration Management, Version Control Systems, repositories o anche
in altri modi, devono essere parte integrante del progetto: bisogna evitare soluzioni
"artigianali" quali, ad esempio, il ricorso a dischi e directory condivisi.
La maggior parte di questi tools sono open source, quindi non è necessario fare investimenti,
se non il tempo necessario ad installare il sistema e comprenderne in maniera almeno
basilare il funzionamento.
Una volta messo in piedi il sistema, assicurarsi che il posto da cui prelevare i sorgenti sia
noto a tutti, nessuno dovrebbe chiedere "Dove è il tale file?", tutto dovrebbe essere nel
repository e rintracciabile.
Tutto il necessario ai fini del funzionamento di un programma eseguibile, deve essere nel
repository, compresi: script di test, file properties, database schema, script di installazione e
librerie esterne.
Nel caso delle librerie esterne, può essere utile l'impiego di tool quali, ad esempio, Maven,
che permettono la gestione centralizzata delle dipendenze tra progetti sollevando, almeno in
parte, gli sviluppatori da tale onere.
E' sconsigliato invece, mantenere nei repositories i prodotti delle build (packages).
L’insieme dei file sorgenti posti sotto controllo di versione, non ancora inclusi in una release
software, viene indicato con vari termini quali, ad esempio trunk, baseline, master. Nel
seguito ci si riferirà a tali file con il termine Mainline.
Automatizzare la Build
Partire dai sorgenti per ottenere un sistema funzionante può spesso essere un processo
complicato che coinvolge compilazione, trasferimento file, caricamento di schemi nei
database, ed altro ancora.
Questa parte può, e deve, essere automatizzata. Chiedere alle persone di eseguire "strani"
comandi o cliccare attraverso finestre di dialogo è una perdita di tempo e può favorire errori.
Gli ambienti automatizzati per le build sono una caratteristica comune dei sistemi (esempio
Ant, Ruby, Nant, MSBuild, ...)
Un errore comune è il non includere tutto nella build automatica. La build dovrebbe
comprendere, ad esempio, il prelievo degli script relativi alle modifiche al database e la loro
esecuzione nell'ambiente in cui il programma verrà mandato in esecuzione.
L'idea di base è che, una volta installata una macchina vergine ex novo, dopo un checkout
del repository questa macchina dovrebbe essere in grado di buildare ed eseguire il
programma senza problemi.
Nel caso la build di un grosso progetto richieda parecchio tempo, particolari accorgimenti
possono essere adottati in modo da non dover costruire da zero il prodotto tutte le volte che
vengono apportate modifiche, anche elementari.
43
4. Continuous Development
Rendere la Build Auto-Testante
Build significa compilazione, link e tutto ciò necessario ad un programma per essere
eseguito partendo dai sorgenti. Un programma può girare, ma ciò non esclude la presenza
di eventuali bug nel codice. Un buon modo per intercettare bug in modo veloce ed efficiente
è includere test automatici nel processo di build. La crescita dell'Extreme Programming (XP)
e del Test Driven Development ha contribuito a diffondere il concetto di codice autotestante. Ovviamente non è strettamente necessario il ricorso al TDD per avere del codice
auto-testante, sicuramente questa tecnica è di grande aiuto nella predisposizione dei test.
E' però necessario avere una suite di test automatici che coprano la maggior parte della
mainline, allo scopo di scoprire eventuali bug. I test vengono poi lanciati tramite un
comando ed il loro risultato deve riportare eventuali test falliti. In una build auto-testante il
fallimento di un test comporta il fallimento della build stessa.
Negli ultimi anni ha acquisito popolarità la famiglia di tool open-source XUnit, ideali per
questo tipo di test e per costruire un ambiente completamente auto-testante.
Ovviamente non si può contare sui test per trovare tutto. Come spesso si dice: i test non
dimostrano l'assenza di bug. Tuttavia, la perfezione non è l'unico punto per cui si ottiene
valore dalla build auto-testante. Test imperfetti, lanciati spesso, sono molto meglio dei test
perfetti che non sono mai stati scritti.
Tutti committano sulla mainline ogni giorno
Integrazione è soprattutto comunicazione. L'integrazione mette gli sviluppatori in
condizione di parlare con gli altri sviluppatori delle modifiche che hanno fatto. Il primo
prerequisito per uno sviluppatore, affinché possa committare il proprio lavoro sulla
mainline, è che questa continui a buildare correttamente. Questo include, ovviamente, il
passaggio dei test automatici. Come avviene per ogni commit, lo sviluppatore prima
aggiorna la propria codebase locale con la mainline su repository, risolve eventuali conflitti,
builda in locale. Se la build va a buon fine, committa le modifiche fatte.
Facendo questo frequentemente, gli sviluppatori scoprono velocemente eventuali conflitti
tra il proprio codice locale e la mainline. La chiave per risolvere i problemi velocemente è
individuarli velocemente. Con commit frequenti, eventuali problemi emergono altrettanto
frequentemente e, a questo punto, non essendovi molte differenze tra due commit adiacenti,
sono relativamente facili da risolvere. I conflitti che rimangono "sommersi" per settimane,
possono essere invece estremamente difficili da risolvere.
Se ci sono solo poche ore di modifiche tra un commit e l'altro, i punti del programma in cui
il problema potrebbe nascondersi rimangono circoscritti.
In generale, più frequentemente si committa, meno punti andranno verificati in caso di
conflitti e, quindi, più rapidamente i conflitti verranno sistemati.
I commit frequenti incoraggiano inoltre gli sviluppatori a spezzare il proprio lavoro in
piccoli intervalli da qualche ora ciascuno. Questo aiuta ad avere consapevolezza dei
progressi fatti.
44
4. Continuous Development
Eseguire tutti i Commit Tests localmente prima di committare
Prima di committare le proprie modifiche sulla mainline è necessario scongiurare il rischio
che queste possano far fallire la build della mainline. Oltre ad aggiornare la propria codebase
locale all’ultima versione della mainline ed assicurarsi che il codice venga correttamente
compilato, è necessario eseguire localmente anche i test che verificano il corretto
funzionamento delle modifiche introdotte (self-testing, vedi sopra). Sono gli stessi test che
verranno eseguiti quando verrà lanciato il processo di build della mainline presente sotto
SCM.
Alcuni moderni server di Continuous Integration sono in grado di gestire automaticamente
questo passaggio. Intercettano le modifiche committate e svolgono automaticamente i test.
Solamente in caso di esito positivo di questi test, le modifiche vengono effettivamente
committate sulla mainline, viceversa viene notificato il problema allo sviluppatore o agli
sviluppatori che l’hanno creato in modo che possa essere risolto.
Ogni Commit lancia una build della mainline su una Integration
Machine
Attraverso commit giornaliere, un team ottiene build testate frequenti. Questo significa che
la mainline, o meglio il prodotto risultante dalla build dei file che la costituiscono, rimane
in uno stato funzionante. Nella pratica, le cose potrebbero andare diversamente. Un motivo
è la disciplina, ovvero persone che non aggiornano la propria codebase locale. Un altro
motivo sono le differenze ambientali tra le macchine dei vari sviluppatori.
Bisogna quindi prevedere build regolari su una macchina dedicata (integration machine)
conseguenti ad ogni commit. E ogni commit viene considerato completato solamente se la
build da lui scatenata va a buon fine. La responsabilità di monitorare che questo accada
ricade sullo sviluppatore che committa, in modo che lui stesso possa sistemare eventuali
inconvenienti. Come corollario, non si esce dall'ufficio se gli ultimi commit di giornata
hanno rotto la build sulla integration machine.
Per assicurare questo, oltre a lanciare build manuali sulla integration machine dopo ogni
build, si può ricorrere ad un continuous integration server.
Un continuous integration server agisce da monitor sul repository. Ogni volta che viene
eseguita una commit sul repository, il server esegue il check out della codebase sulla
integration machine, lancia la build e notifica a chi ha committato l'esito della build. Il
commit si considera concluso solo a ricezione avvenuta della notifica. Onde evitare spam di
notifiche, si può restringere l'invio di una notifica solamente in caso di fallimento della build.
E' comunque responsabilità dello sviluppatore, verificare l'esito della build avvenuta sulla
integration machine in seguito al suo commit.
Alcune organizzazioni ricorrono a build schedulate, questo però è leggermente diverso
rispetto al concetto di continuous build esposto sopra e non è sufficiente come pratica di
Continuous Integration, in quanto uno degli scopi della continuous integration è scovare
problemi in tempo brevissimo. L'emergere di un bug durante una build notturna, ad esempio,
45
4. Continuous Development
significa che, potenzialmente, questo bug è rimasto nascosto per un intero giorno lavorativo,
e potrebbe essere quindi di non facile risoluzione.
Non iniziare altre attività fino a quando non vengono superati i Commit
Tests
Il sistema di Continuous Integration è una risorsa condivisa per il team. Se viene
effettivamente impiegato come descritto poc’anzi, con commit frequenti, ogni rottura della
build implica un temporaneo blocco per il team e, di conseguenza, per il progetto. Questi
inconvenienti, tuttavia, sono normali e da mettere in conto. L'importante è quindi che gli
errori che li hanno causati vengano individuati e riparati nel minor tempo possibile.
Al momento del commit gli sviluppatori che lo hanno eseguito sono responsabili del
monitoraggio della build della mainline. Fino a quando la compilazione della mainline non
è terminata ed i commit tests sono stati eseguiti e superati sulle macchine dedicate alla
Continuous Integration, gli sviluppatori non dovrebbero iniziare nuove attività, riunioni e
pause pranzo incluse.
Se il commit va a buon fine, sono allora liberi di dedicarsi a nuove attività. Se fallisce sono
già pronti per determinare la natura del problema e per risolverlo, con un nuovo commit o
con un revert alla situazione precedente al commit che ha rotto la build. In questo secondo
caso, le modifiche vengono quindi rimosse dalla mainline fino a quando non verranno
ricommittate funzionanti.
Non committare se la build è rotta
Uno dei "peccati capitali" della Continuous Integration è il commit sulla build rotta. Se la
build si rompe, gli sviluppatori che hanno causato la rottura stanno lavorando, o almeno
dovrebbero, per risolvere il problema al più presto.
Se dei colleghi committano una o più modifiche che rompono la build (non facendola
compilare o facendo fallire dei test automatici, per esempio), devono poter risalire al
problema senza ostacoli, per risolverlo nel migliore dei modi. Ulteriori commit di modifiche,
scateneranno nuove build che falliranno ed i cui risultati potrebbero mescolarsi con quelli
della prima build che si è rotta, creando confusione.
Quando non viene rispettata questa regola, quindi, il fix della build richiede maggior tempo
e, come conseguenza, le persone si "abituano" a vedere la build rotta e si arriva ad una
situazione in cui la build rimane rotta per la maggior parte del tempo. Questo continua fino
a che qualcuno nel team decide che "quando è troppo è troppo" e con un notevole sforzo
ripara la build.
Mai andare a casa se la build è rotta
Lasciare la build rotta a fine giornata lavorativa, o peggio, a fine settimana non è una buona
pratica. Al rientro in ufficio potrebbe esser necessario diverso tempo per rifocalizzare
46
4. Continuous Development
l'attenzione su quel che potrebbe aver rotto la build. Inoltre, il resto del team sarebbe
costretto ad uno stop forzato ad inizio giornata o settimana lavorativa o peggio, a dover
investire energie per aggirare il problema. Nel caso dei team distribuiti in punti con fusi
orari diversi questo aspetto assume ancora maggiore criticità.
Per essere chiari, non è richiesto di rimanere in ufficio fino ad orari improponibili per
sistemare una build, viceversa è raccomandato fare commit frequenti e lontani da orarilimite, in modo da aver tempo di fronteggiare eventuali anomalie. In alternativa, attendere
il giorno successivo per committare. Molti sviluppatori esperti e molti team Agili
definiscono un orario oltre il quale non si committa (ad esempio un'ora prima della fine
dell'orario lavorativo) e tutti i commit non fatti oltre l’orario limite diventano la prima
attività da svolgere nella prossima giornata lavorativa.
Sistemare immediatamente le build rotte
Questo è diretta conseguenza di quanto appena esposto.
Se la build della mainline fallisce, deve essere sistemata immediatamente. Non è un fatto
negativo in se "rompere la build", anche se una perseveranza di questo potrebbe indicare
scarsa attenzione da parte degli sviluppatori nella fase pre-commit (build in locale su tutto).
Quando questo avviene, il ripristino della build assume priorità massima. Non è necessario
che tutto il team si dedichi a ciò, di norma bastano una o due persone.
Spesso il metodo più rapido per sistemare la build è ripristinare la situazione all'ultimo
commit fatto prima che si manifestasse il problema, riportando di fatto il sistema indietro.
A meno che la causa del problema sia evidente, è buona norma lasciare la mainline
aggiornata all'ultimo commit funzionante e ricercare il problema che ha rotto la build
eseguendo debug su una singola workstation.
Essere sempre pronti a tornare alla versione precedente
Come abbiamo visto, la rottura della build è una cosa normale. Nei progetti di dimensioni
importanti, è lecito aspettarsi che ciò avvenga almeno una o due volte al giorno, nonostante
i test svolti in locale prima di committare modifiche allevino questo rischio.
In queste circostanze, i fix normalmente consistono in commit di poche righe di codice che
risolvono bug in tempo rapidissimo.
Tuttavia, a volte, il compito è più arduo sia per un errore un po' più "grave" del solito, e
questo non significa colpevolizzare chi l'ha commesso, sia perché il bug non è di semplice
individuazione e sia perché subito dopo il commit delle modifiche ed il successivo fail della
build, si potrebbe realizzare di aver trascurato particolari importanti nell'implementare le
modifiche appena committate. Indipendentemente dalla ragione, è importante ripristinare il
corretto funzionamento della build alla svelta. Se il problema, per qualunque ragione, non
può essere risolto rapidamente, occorre riportare la mainline alla situazione precedente al
commit che ne ha rotto la build facendo revert della modifica.
47
4. Continuous Development
L’approccio mentale suggerito è lo stesso, facendo un paragone, che hanno i piloti di aereo
quando stanno per atterrare. Ovvero essere pronti a tornare indietro (“go around”) e fare un
ulteriore tentativo nel caso qualcosa vada storto.
Definire un Time-Box per il fix della build rotta
Quando la build si rompe, investire non più di X minuti per sistemarla. Se dopo X minuti
non è stata sistemata, procedere col revert della mainline alla versione precedente alla
rottura, recuperabile dal versioning control system. L’entità di X è lasciata all’autoregolamentazione dei team di sviluppo. In alcuni casi, se trascorso questo limite ci si sente
confidenti di essere prossimi alla soluzione, si può ricorrere ad alcuni minuti extra. Ad
esempio, se dopo dieci minuti si stà procedendo con la build locale, è ovviamente possibile
terminare la build locale, procedere col commit e, nel caso la build sia tornata in uno stato
funzionante, il fix si può considerare finito. Viceversa, procedere col revert e, con calma, a
tutti i controlli del caso.
Mantenere la build rapida
Uno degli scopi della Continuous Integration è il fornire un rapido feedback. Nulla è più
"stressante" di una build che dura "a lungo". Sul concetto di "build lunga" si potrebbe
discutere per giorni. Dipende dai punti di vista e dalle abitudini dei team. Alcuni potrebbero
percepire come lunga una build della durata di un'ora, mentre altri sognano di avere una
build che duri "solo" un'ora.
Le linee guida dell'Extreme Programming definiscono 10 minuti come durata ragionevole
di una build e i progetti più moderni rispettano queste linee. Vale la pena sforzarsi affinché
ciò avvenga. Ogni minuto di riduzione della build, è un minuto "regalato" agli sviluppatori
ogni volta che loro committano (e che devono attendere l'esito della build della mainline...).
Non sempre è possibile ridurre "magicamente" l'intero processo di build alla durata
desiderata.
La pratica consigliata, è l'impostazione di una deployment pipeline. L'idea dietro alla
deployment pipeline (nota anche come build pipeline o staged build) è che, di fatto, ci sono
build multiple eseguite in sequenza. Il commit sulla mainline scatena la build primaria, detta
anche commit build, che è quella che deve essere veloce.
Una volta che la commit build è a posto, le altre persone possono lavorare sul codice con
confidenza. Tuttavia ci possono essere ulteriori test, più lenti, che devono essere svolti.
Questi possono, ad esempio, essere demandati a macchine dedicate e/o eseguiti in tempi
diversi.
Un semplice esempio è una deployment pipeline a due stadi. Il primo si occupa della commit
build, ovvero della compilazione e dell'esecuzione dei test unitari che non dipendono dai
dati presenti su database. Questi test sono molto veloci e mantengono tutto il processo sotto
i 10 minuti stabiliti. Tuttavia alcuni bug potrebbero non essere scovati dai test unitari, in
particolare quelli eventualmente presenti in parti che richiedono interazioni più di larga
scala, come ad esempio interazioni col database.
48
4. Continuous Development
La build svolta al secondo livello (build secondaria) lancia una suite differente di test, più
lenti, che verificano il comportamento del codice su dati presi dal database reale, testando
così il comportamento del codice nel suo insieme piuttosto che nelle sue singole parti.
In questo scenario, il primo livello viene usato come ciclo principale di Continuous
Integration. La build secondaria invece, viene eseguita (o schedulata) quando possibile,
prelevando il codice eseguibile preparato dall'ultima build di primo livello andata a buon
fine e svolgendo test ulteriori. Se questa seconda build fallisce, l'impatto sul team non è
importante quanto quello che avrebbe un fallimento della commit build, tuttavia deve essere
sistemata in tempi ragionevolmente rapidi.
Un bug rilevato dalla build secondaria spesso riguarda un problema rilevabile anche da uno
o più test unitari eseguiti dalla commit build. In generale, ogni problema emerso ad un livello
successivo al primo, porta allo sviluppo di nuovi test unitari da eseguire durante la commit
build in modo da renderla sempre più efficace ed abbassare il rischio che i livelli successivi
rilevino bug o malfunzionamenti.
Il principio può essere esteso a più livelli successivi. Le build secondarie possono anche
essere eseguite in parallelo. Ad esempio se i test secondari durano due ore, si possono
migliorare le performances distribuendoli equamente su due macchine che lavorano in
parallelo. Parallelizzando le build secondarie in questo modo si può introdurre ogni sorta di
test automatici all'interno del processo di build.
Testare in un clone dell’ambiente di Produzione
Lo scopo dei test è (anche) quello di eliminare ogni problema che il sistema potrebbe avere
in produzione. L'ambiente in cui il sistema si troverà a girare in produzione è molto
importante. Se i test vengono svolti in un ambiente differente, ogni differenza è sinonimo di
rischio che quello che succede in ambiente di test non succederà in produzione.
La pratica ideale sarebbe quindi quella di impostare l'ambiente di test il più possibile uguale
a quello di produzione. Utilizzare lo stesso software, alla stessa versione per il database, la
stessa versione del sistema operativo. Tutte le librerie presenti nell'ambiente di produzione
devono essere riportate nell'ambiente di test, anche se il sistema non le usa. Utilizzare gli
stessi indirizzi IP, le stesse porte e lo stesso hardware.
In realtà ci sono dei limiti. Se si scrive, ad esempio, software per macchine desktop è
praticamente impossibile testare su un clone di tutte le potenziali macchine su cui il sistema
verrà installato (sic!). Similmente, alcuni ambienti di produzione sono molto costosi,
impensabile quindi la loro duplicazione.
Nonostante questi limiti, l'obiettivo rimane quello di replicare l'ambiente di produzione al
meglio e comprendere i rischi legati ad ogni differenza tra l'ambiente di test e quello di
produzione.
Negli ultimi tempi ha acquisito sempre maggior popolarità il ricorso alla virtualizzazione,
vale a dire più ambienti simulati su una stessa macchina fisica. Risulta quindi relativamente
facile impostare uno o più ambienti virtuali all'interno dei quali svolgere i test.
49
4. Continuous Development
Non escludere i test che falliscono
In presenza di una build rotta a causa del fallimento di alcuni test, gli sviluppatori tendono
a commentare il codice di questi test, bloccandone l’esecuzione, in modo da poter
committare agevolmente le proprie modifiche ed avere la build correttamente funzionante.
Questo è comprensibile, ma sbagliato. Quando uno o più test che hanno sempre funzionato
falliscono, può essere complicato scoprirne il motivo. Siamo veramente in presenza di
regressione? Magari una o più assunzioni fatte dai test non sono più valide, oppure sono
state fatte modifiche alla funzionalità testata per un motivo valido. Scoprire quale di queste
condizioni si è verificata può coinvolgere più persone per un discreto periodo di tempo, ma
è essenziale scoprire cosa stà succedendo e quindi sistemare il codice se si è introdotta
regressione, modificare i test se la funzionalità ha cambiato comportamento o addirittura
eliminarli nel caso non servano più.
L’esclusione dei test che falliscono deve essere vista come una extrema-ratio, a cui si deve
ricorrere, ad esempio, se è necessario un importante ed articolato sviluppo.
Assumersi le proprie responsabilità
Assumersi la responsabilità degli inconvenienti che i propri commit potrebbero causare alla
build della mainline.
Se i test scritti per la funzionalità introdotta o modificata passano, ma altri no, significa che
è stata introdotta regressione.
E’ responsabilità di chi ha sviluppato questa funzionalità sistemare anche gli altri test che in
seguito alla modifica hanno smesso di avere esito positivo.
Questa pratica ha diverse implicazioni. In primis gli sviluppatori devono avere accesso a
tutto il codice impattato dalla propria modifica, in modo da poter sistemare ciò che si
dovesse rompere. Questo esclude la possibilità che gli sviluppatori abbiano accesso
esclusivo su una parte ristretta della codebase. Nella pratica di Continuous Integration, tutto
il team ha accesso alla intera codebase. Nel caso questo non sia proprio possibile per cause
non facilmente risolvibili, si deve per forza ricorrere alla collaborazione da parte di chi può
mettere mano a certe parti della codebase.
Rendere gli ultimi eseguibili creati accessibili facilmente
Una delle parti più difficili dello sviluppo software è assicurare che venga buildato il
software corretto. Abbiamo visto che è difficile specificare i requisiti corretti in anticipo. Le
persone trovano molto più semplice notare qualcosa fatto in modo non completamente
corretto e indicare i cambiamenti necessari. I processi Agili sono basati anche su questo
comportamento umano.
Per aiutare in questo, chiunque è coinvolto in un progetto software deve poter avere gli
ultimi eseguibili creati ed essere in grado di mandarli in esecuzione: per dimostrazioni, test
esplorativi o anche semplicemente curiosità.
Fare questo è abbastanza semplice: basta assicurarsi che ci sia un posto noto dove le persone
possono trovare gli ultimi eseguibili. Può essere utile mettere diversi eseguibili in questo
50
4. Continuous Development
posto. Oltre all'ultimissima versione, si potrebbe mettere anche l'ultima che ha passato i
commit test, qualora questi test non fossero vincolanti al processo di build e costruzione
degli eseguibili.
Se si segue un processo di sviluppo per iterazioni ben definite (sprint), è utile mettere anche
l'eseguibile prodotto a seguito degli sviluppi fatti durante le ultime iterazioni concluse. In
particolare le dimostrazioni necessitano di software con caratteristiche conosciute, in queste
circostanze vale la pena sacrificare l'ultimissima versione disponibile a discapito di una
versione le cui caratteristiche siano note a chi la deve presentare.
Chiunque è informato sulla build
La Continuous Integration si basa sulla comunicazione, bisogna quindi assicurare che tutti
siano informati circa lo stato del sistema e delle modifiche che vi sono state apportate.
Una delle cose più importanti da comunicare è lo stato della build della mainline. Diversi
tool per la gestione della Continuous Integration espongono interfacce web che forniscono
questa informazione.
I team, nell'ambito della propria auto-organizzazione, possono assicurare queste
informazioni adottando gli accorgimenti che ritengono opportuni. Alcuni team, ad esempio,
connettono alle macchine adibite alla continuous integration dispositivi luminosi o sonori
che emettono opportuni segnali (esempio luce rossa vs. luce verde, oppure fischi vs.
applausi, o anche peggio...) a seconda dello stato della build.
Questo aspetto assume importanza strategica laddove si adotta un approccio manuale verso
la Continuous Integration. Deve essere chiaro a tutto il team chi è il collega che ha la
temporanea responsabilità della build della mainline.
I server Continuous Integration possono fornire ulteriori informazioni rispetto al semplice
stato della build. Ad esempio possono fornire informazioni circa le modifiche apportate alla
mainline ed i loro autori.
Non dimentichiamo che le informazioni esposte via web possono essere utili per i team
distribuiti.
Automatizzare il Deployment
Per fare Continuous Integration servono diversi ambienti, uno per i commit tests, uno o più
per i test secondari. Dal momento che vengono trasferiti eseguibili tra questi ambienti, più
volte al giorno, è ideale farlo automaticamente. E' quindi importante avere script che
permettano di deployare facilmente l'applicazione in ogni ambiente utile ai fini di test.
Come conseguenza naturale di questo, si dovrebbero avere a disposizione script che
permettono il deploy in produzione con la stessa facilità. Può non essere necessario
deployare in produzione ogni giorno, ma avere a disposizione script per il deploy automatico
aiuta ad accelerare il processo ed a ridurre gli errori.
Se si procede col deploy automatico in produzione, occorre considerare anche il rollback
automatico. Bisogna essere in grado di poter riportare l'ambiente di produzione alla versione
51
4. Continuous Development
precedentemente installata e funzionante, nel caso emergano malfunzionamenti non rilevati
in sede di test. La possibilità di poter svolgere automaticamente questo passo indietro riduce
la tensione all'interno del team e del dipartimento, incoraggia le persone a deployare più
frequentemente, e quindi a fornire agli utenti nuove funzionalità rapidamente.
Oltre al trasferimento degli eseguibili tra i server, gli script introdotti precedentemente
hanno anche responsabilità di apportare eventuali modifiche all’ambiente in cui
l’applicazione dovrà operare. Devono quindi essere incluse in questi script le modifiche al
sistema operativo, ai server web ed alle macchine virtuali, se previste, al database. Le stesse
responsabilità, ovviamente al contrario, devono essere assunte dagli script di rollback.
Questi processi possono essere gestiti sia tramite “semplici” script bash o batch, che tramite
l’impiego di tool atti allo scopo (Ansible, Puppet, ...) alcuni dei quali (Docker) orchestrano
il rilascio completo dell’applicazione e di “tutto quel che le serve per funzionare” su di un
server web.
Da dove cominciare?
Se in condizioni ottimali, quali ad esempio un progetto in fase di avvio, uno o più team di
sviluppo software neo costituiti, e così via, implementare queste pratiche può essere una
attività relativamente semplice. Nella maggioranza dei casi, però, il team di sviluppo e/o
l’azienda si trovano a volere adottare metodologie di Continuous Development partendo da
una situazione più o meno tradizionale. La domanda “Da dove cominciare?” sorge quindi
spontanea. Le pratiche appena elencate portano tutti i benefici possibili, ma non per forza è
necessario iniziare implementandole tutte.
Non c’è una ricetta fissa, molto dipende dal team di sviluppo e dalla natura del sistema su
cui si va ad implementare il Continuous Development. Di seguito una sequenza di azioni da
intraprendere che potrebbe essere impiegata nella maggior parte dei processi di adozione
del Continuous Development.
Un primo passo potrebbe essere l’automatizzazione della build. Mettere tutto il necessario
(sorgenti ma non solo) sotto versionamento in modo da poter lanciare la build con un singolo
comando. Per molti progetti non è una impresa semplice ed è essenziale per il buon
funzionamento di tutte le altre cose. Inizialmente si può partire con build occasionali
lanciando manualmente il comando oppure con build automatiche notturne. Una build
automatica notturna è un buon punto di partenza.
Successivamente, introdurre alcuni test automatici nel processo di build. Identificare le aree
dove si verificano malfunzionamenti più frequenti e predisporre test automatici che siano in
grado di evidenziarli. Sui progetti esistenti è difficile avere in tempi rapidi una buona suite
di test, predisporli richiede tempo. Da qualche parte si dovrà pur cominciare (“Roma non fu
fatta in un giorno”).
Successivamente, provare ad accelerare la commit build. Continuous Integration su una
build della durata di qualche ora è meglio di niente, ma portare la durata della build ai dieci
magici minuti è molto meglio. Questo di solito richiede parecchia “chirurgia” sulla code
base al fine di eliminare le dipendenze dalle parti lente del sistema.
52
4. Continuous Development
Se si stà invece iniziando un nuovo progetto, applicare metodologie Continuous
Development dal principio. Tenere sotto controllo le durate delle build ed intervenire non
appena queste superano la regola dei dieci minuti. Intervenendo rapidamente, si metteranno
in essere tutte le necessarie ristrutturazioni prima che la code base diventi così grande da
rendere dolorose le sue ristrutturazioni.
Ma soprattutto, cercare aiuto. Trovare qualcuno che abbia esperienza in tema Continuous
Development, avendovi già lavorato in passato è di grande aiuto. Come ogni nuova tecnica,
è difficile introdurla quando non si sa come dovrebbe apparire il risultato finale.
I vantaggi
Il vantaggio più grande e più ad ampio raggio del Continuous Development è la riduzione
dei rischi.
Vicolo Cieco
Il problema dell’integrazione differita suggerita dalle metodologie classiche, è che è difficile
predire quanto durerà e, peggio, è difficile percepire a che punto si è all’interno del processo.
Il risultato è che ci si pone in un vicolo cieco durante una delle parti cruciali del processo di
sviluppo di un prodotto software.
Il Continuous Development elimina questo problema. Non c’è processo di integrazione
lungo, si elimina completamente il vicolo cieco. Ad ogni momento si sa a che punto si è,
cosa funziona e cosa no, ovvero si conoscono i bug presenti nel sistema.
Bugs
I bug sono ciò che tipicamente distrugge confidenza e sicurezza e scompiglia pianificazioni
e reputazioni. I bug nel software rilaciato rendono gli utenti “arrabbiati”. I bug durante lo
sviluppo ostacolano i programmatori, rendendo difficile il corretto funzionamento del resto
del software.
Il Continuous Development non libera dai bugs, ma li rende molto più facili da individuare
e rimuovere. A tal riguardo è quasi come avere codice auto testante (self-testing). Se si
introduce un bug e lo si individua alla svelta, sarà semplice anche la sua rimozione. La
facilità con cui il bug viene individuato deriva direttamente dal fatto che uno o comunque
pochi sviluppatori hanno lavorato su una piccola porzione del sistema, apportando piccole
modifiche (principio delle modifiche incrementali unito ai commit, ed ai relativi test,
frequenti) e che la parte di sistema modificata sarà per forza di cose quella più “fresca” nella
mente di chi ha sviluppato le modifiche.
I bug sono cumulativi. Più bugs si hanno, più difficile è rimuoverli. Questo è dovuto in parte
al fatto che si possono avere interazioni tra bug, situazioni in cui gli errori sono il risultato
di errori multipli, che rendono quindi difficile l’individuazione di ogni singolo errore, ed in
parte a questioni psicologiche. Gli sviluppatori hanno meno energia per individuare e
53
4. Continuous Development
correggere bug quando ce n’è più di uno, un fenomeno che viene talvolta individuato come
“The Broken Windows Syndrome”.
Come risultato i progetti con Continuous Development tendono ad avere molti meno bug,
sia in produzione che in sviluppo. Questo beneficio è sempre rapportato alla bontà della
suite di test. Per raggiungere un livello di bug sufficientemente basso è necessario investire
costantemente tempo sull’ampliamento e sul miglioramento della suite di tests.
Il deploy frequente è prezioso all’interno del ciclo di sviluppo, soprattutto perché permette
agli utenti di avere nuove caratteristiche più rapidamente e di fornire un feedback circa
queste nuove caratteristiche altrettanto rapidamente.
Questo aiuta ad abbattere una tra le più grandi barriere che ostacolano un processo di
sviluppo software di successo, ovvero le barriere tra clienti (intesi come committenti,
Business Owners, Stakeholders, etc…) e sviluppatori.
L’evoluzione ulteriore: DevOps
Nelle organizzazioni tradizionali le funzioni “Development”, ovvero gli sviluppatori, e
“Operations”, altri professionisti IT (ad esempio i sistemisti) sono distinte. Semplificando,
possiamo dire che le prime si occupano dello sviluppo software, le seconde del rilascio in
produzione e del corretto funzionamento di quanto rilasciato.
Le funzioni hanno obiettivi diversi che, paradossalmente, rischiano di entrare in conflitto.
Gli sviluppatori (Developers) mirano a rilasciare in fretta funzionalità nuove o migliorate, e
quindi rilascerebbero software ogni giorno, mentre le Operations puntano ad avere il sistema
sempre funzionante ed efficiente e tendono mantenere le cose allo stato attuale (funzionante)
il più a lungo possibile. Questa differenza tende a rallentare i rilasci, e quindi il Business.
DevOps è la combinazione tra i due termini, “Development” e “Operations”. Con questo
termine ci si riferisce ad una metodologia
di sviluppo software che enfatizza al
massimo
la
collaborazione,
la
comunicazione e l’integrazione tra
sviluppatori software e professionisti IT
(sysadmins, DBA, etc.), come descritto
su (da Wikipedia [17]). Lo scopo di
questa
metodologia
è
mettere
un’organizzazione in grado di fornire
prodotti e servizi software in tempi
rapidi, evitando i “conflitti” descritti
sopra.
Una volta adottate le metodologie Agili,
permane comunque una separazione
abbastanza netta tra i reparti Sviluppo, IT
operations e servizio testing, controllo ed
assicurazione qualità (QA). In genere, la Figura 21 DevOps significa collaborazione tra reparti
separazione è tra le attività di sviluppo e
54
4. Continuous Development
quelle connesse al rilascio. La metodologia DevOps promuove un set di processi ed
accorgimenti atti a favorire comunicazione e collaborazione tra i reparti. Processi ed
accorgimenti dipendono dall’organizzazione e dagli obiettivi che si intendono perseguire.
Nell’ambito di una organizzazione che eroga un servizio web, un esempio di DevOps
potrebbe essere una collaborazione stretta tra Sviluppatori e Tester al fine di predisporre una
suite di test automatici e manuali a garanzia e verifica di quanto sviluppato, e tra Sviluppatori
e Sistemisti al fine di predisporre uno script automatico che permetta il rilascio in vari
ambienti del software sviluppato.
55
4. Continuous Development
56
5 . L'impostazione del processo in Volagratis
5 . L'impostazione del processo in Volagratis
Volagratis è un sito specializzato nella ricerca e comparazione di voli e di una vasta gamma
di prodotti e servizi legati al viaggio. Lanciato in Italia nel 2004 da Fabio Cannavale e Marco
Corradino, Volagratis è stato uno dei primi motori di ricerca specializzati in voli low-cost.
Nel corso degli anni l'offerta è stata ampliata a numerosi prodotti e servizi legati al viaggio
che possono essere ricercati, confrontati e prenotati in maniera semplice e veloce sulla sua
piattaforma online e - più recentemente - tramite le mobile app.
Volagratis è parte di Bravofly Rumbo Group, uno dei principali operatori europei nel settore
dei viaggi online con oltre 7 milioni di visitatori unici al mese, e più di 4,5 milioni di
passeggeri serviti all’anno. Attraverso le sue piattaforme web e mobile, disponibili in 15
lingue, gli utenti possono cercare e prenotare voli, hotel, pacchetti vacanza, crociere, auto a
noleggio e molti altri prodotti, trovando in maniera semplice e rapida la soluzione più adatta
alle loro esigenze.
Bravofly Rumbo Group opera in oltre 35 Paesi in tutto il mondo e punta nei prossimi anni
ad ampliare e rafforzare la propria presenza globale per diventare un punto di riferimento
chiave nel settore del turismo e il tempo libero (tratto dalla descrizione ufficiale di
Volagratis).
Dal punto di vista tecnico, il motore di ricerca e prenotazioni voli presenta agli utenti una
interfaccia unica per ricercare e prenotare voli forniti da sistemi eterogenei, non standard.
La richiesta fatta dall’utente del sistema viene tradotta in N richieste diverse inviate a N
differenti sistemi. I risultati vengono poi normalizzati e presentati in interfaccia unica
all’utente, che può selezionarne e prenotarne uno. Anche il processo di prenotazione è
normalizzato, viene raccolto un set standard di dati dell’utente, che vengono poi tradotti in
una richiesta appropriata ed indirizzata al sistema fornitore del volo in prenotazione.
57
5 . L'impostazione del processo in Volagratis
Architettura del sistema
La prima versione del sistema, sviluppata nel 2004 da un singolo sviluppatore, aveva una
architettura monolitica. Una singola applicazione web, sviluppata in Java, con largo impiego
dei framework Struts e Spring, installata su container web Tomcat, supportata da un
database mysql con schema unico, conteneva sia la parte di FrontEnd, che esponeva
all’utente una maschera di ricerca voli e comparazione risultati che la logica necessaria per
reperire le informazioni richieste dall’utente presso un fornitore esterno.
Figura 22 Schema dell’architettura del sistema agli inizi dell’attività del sito (2004)
Successivamente il sistema si è evoluto e nuove funzionalità sono state introdotte, tra cui la
possibilità di prenotare voli, che è il business principale del sito al giorno d’oggi, ricercare
e prenotare hotel, pacchetti vacanza, crociere, auto a noleggio e servizi ausiliari in generale.
58
5 . L'impostazione del processo in Volagratis
Figura 23 La home page del 2004 (da web.archive.org) e quella attuale (2015) a confronto.
Assieme ad un aumento delle funzionalità e dell’offerta commerciale, il portale ha
conosciuto nel corso dei suoi primi 10 anni di attività un notevole aumento nel volume di
traffico e del numero di prenotazioni giornaliere concluse. Questo ha comportato una
crescita del numero di sviluppatori software impiegati nel progetto, che da 6 sono
59
5 . L'impostazione del processo in Volagratis
gradualmente diventati più di 200, distribuiti su diverse sedi, tutti alle dipendenze
dell’azienda che gestisce il portale.
Parallelamente, il sistema ha conosciuto una evoluzione architetturale. Il sistema monolitico
iniziale è stato suddiviso in moduli funzionali distinti che interagiscono tra loro. Questa
evoluzione ha portato sicuramente dei benefici. Nonostante questo i moduli rimangono
abbastanza monolitici ed alcune loro funzionalità non sono di immediata manutenzione,
soprattutto da parte degli sviluppatori meno esperti.
Figura 24 Schema architettura attuale. Per ragioni di semplicità si è illustrata solamente la parte relativa a ricerca
e prenotazione Voli
Tutti i moduli sono applicazioni web (war) sviluppate in Java, che comunicano tra loro
tramite protocollo SOAP o RPC. L’architettura software su cui si basano è la medesima,
salvo alcune lievi differenze secondarie. Semplificandola, possiamo descriverla in questo
modo:
Sistema Operativo: ogni macchina che ospita un modulo applicativo è unix-based, il
sistema operativo è Linux. Tutte le macchine sono virtuali.
● Server Web: gli applicativi web dei moduli, girano in container web Apache
Tomcat. I vari Tomcat sono posti sotto bilanciamento di carico.
● Database: il DBMS è MySql. Lo schema unico iniziale è stato diviso in più
schemi funzionali ed i vari schemi sono stati suddivisi su diversi server. Alcuni
60
5 . L'impostazione del processo in Volagratis
moduli, soprattutto quelli i cui cambiamenti sono meno frequenti, condividono
ancora la base dati tra loro.
I moduli soggetti a volumi di traffico maggiore sono replicati su più macchine. E’ compito
di un Load Balancer smistare il traffico in entrata verso una opportuna istanza
dell’applicazione web.
Investimenti consistenti sono stati fatti sia in termini infrastrutturali per ampliare le risorse
hardware a disposizione dei vari moduli, sia in termini di formazione del personale. Sono
state introdotte le metodologie Agili, l’Extreme Programming e Scrum.
Ogni modulo è soggetto a piccoli miglioramenti incrementali, apportati da uno o più team
Agili di sviluppatori nel corso di sprint che durano tra le due e le quattro settimane.
AS-IS: il punto di partenza
Il punto di partenza è rappresentato dai moduli attuali. Per ragioni di semplicità ci
interesseremo al solo flusso di ricerca e prenotazione voli ed ai moduli coinvolti in tale
flusso.
Occorre fare una premessa importante. Il sistema con cui vengono gestite ricerche e
prenotazioni voli varia significativamente tra le compagnie di linea tradizionali (quali ad
esempio Lufthansa, British Airways, etc. etc.) e le compagnie low-cost (ad esempio Vueling,
Wizzair, etc.). Le prime pubblicano le proprie tariffe su sistemi integrati definiti GDS,
ovvero Global Distribution System (Amadeus, Galileo e Sabre i più noti). Tramite GDS è
possibile effettuare la ricerca tariffe, la creazione e la finalizzazione della pratica di
prenotazione. Creazione e finalizzazione, sono due processi separati che possono avvenire
a distanza di tempo. Mentre le compagnie low cost gestiscono autonomamente il processo
di ricerca e prenotazione e non prevedono la possibilità di creazione della prenotazione e
sua finalizzazione in un secondo momento. La prenotazione di un volo low cost è possibile
soltanto tramite sito web o call center della compagnia.
In tutto questo esistono, e non sono rare, le eccezioni in un senso, ovvero compagnie di linea
che prevedono ricerca e prenotazione tramite sistemi paralleli al GDS (ad esempio Aer
Lingus), e nell’altro con compagnie definite lowcost che offrono la possibilità di ricerca e
prenotazione delle proprie tariffe (con regole diverse e generalmente meno economiche)
tramite GDS. Air Berlin e Vueling sono compagnie low cost prenotabili anche tramite GDS.
Calando tutto questo nel sistema Volagratis, i GDS fungono da tramite tra il sistema
Volagratis e le Compagnie aeree tradizionali. L’interazione con le compagnie low cost
avviene invece via web services, direttamente con loro, previa stipula di un contratto.
61
5 . L'impostazione del processo in Volagratis
Applicazione web Volagratis
Si tratta dell’applicazione alla base del sito web, entry point principale di tutto il sistema.
L’applicazione è responsabile di tutta la parte di front-end del sito. Connettendosi al sito
web, gli utenti possono effettuare una ricerca voli. I parametri di ricerca vengono passati al
modulo BSA che risponderà fornendo le possibili soluzioni, che verranno mostrate
all’utente in pagina risultati. A questo punto l’utente ha possibilità di selezionare una tra le
soluzioni proposte ed avviare il processo di prenotazione. In tal caso, viene invocato
nuovamente il modulo BSA per una verifica della effettiva disponibilità e della tariffa della
soluzione desiderata dall’utente. Eventuali variazioni di tariffa (più o meno “normali” nel
settore della biglietteria aerea) vengono comunicate al cliente dall’applicazione.
Qualora il cliente decida di proseguire con la prenotazione, l’applicazione curerà la raccolta
dati necessari alla finalizzazione, compresi gli estremi per il pagamento. Una volta che si
hanno a disposizione tutti i dati viene fatta un’ultima chiamata al modulo BSA per la
finalizzazione della prenotazione.
Una volta completato il processo di acquisto, l’applicazione mostrerà all’utente la pagina di
conferma prenotazione e provvederà all’invio della mail di conferma.
Figura 25 Sequence Diagram del processo di ricerca fatto usando l’applicazione Volagratis
62
5 . L'impostazione del processo in Volagratis
Applicazione web services
In aggiunta all’applicazione web, è disponibile un’interfaccia web services. Questi servizi
vengono messi a disposizione di applicazioni esterne client (ad esempio i comparatori di
prezzi ed offerte) che intendono fornire ai propri utenti i voli e le tariffe offerte dal sistema
Volagratis, utilizzando però la propria interfaccia front-end.
Tramite web services è possibile sia invocare solamente il servizio di ricerca e poi
concludere la prenotazione invocando l’applicazione volagratis tramite un apposito url,
oppure gestire completamente il processo di prenotazione tramite chiamate web services.
L’applicazione web services è quella con cui si interfacciano le app mobile per ricerca e
prenotazione voli.
Figura 26 Screenshots dell’app mobile Volagratis. Alimentata dall’applicazione Web Services
Business Scanner Architecture (BSA)
E’ il modulo che mette in comunicazione le applicazioni Volagratis e WebServices con i
sistemi di biglietteria aerea (GDS) o le compagnie aeree (integrazione diretta).
E’ installato come applicativo web su server dedicati e viene invocato dalle applicazioni
Volagratis e Webservices tramite chiamate Rpc. L’interazione con le compagnie aeree e con
63
5 . L'impostazione del processo in Volagratis
i sistemi GDS non avviene in modo diretto, bensì è moderata da sistemi intermedi, che sono
altre applicazioni web sviluppate internamente quali Spiderfly e l’integrazione con i sistemi
GDS.
E’ responsabile sia del processo di ricerca che di quello di prenotazione presso questi
sistemi. Una volta ricevuti dal layer logico i dati relativi alla ricerca richiesta dall’utente
dell’applicazione Volagratis o dei Web Services, stabilisce verso quali altri moduli interni
redirigere la ricerca in funzione di alcune logiche di business, dopodiché funge da collettore
dei risultati ottenuti e li restituisce al layer logico. Effettua anche la normalizzazione delle
ricerche e delle risposte, convertendo le prime dallo standard Volagratis a quello accettato
dal modulo chiamato e viceversa per le seconde. Nonostante i moduli invocati siano interni,
hanno uno standard per richieste e risposte differente rispetto a quello Volagratis/Bsa.
Oltre alla ricerca, questo modulo viene invocato ogni volta che è necessaria una verifica di
disponibilità e tariffa e quando occorre finalizzare il processo di acquisto presso il GDS o la
compagnia aerea. Ricevuta in input una richiesta, vengono svolte determinate operazioni
coinvolgendo il modulo o il sistema esterno presso il quale deve essere svolta la verifica di
tariffa e disponibilità o deve essere finalizzata la prenotazione.
Spiderfly
Con alcune compagnie aeree, soprattutto low cost, sono in essere accordi commerciali per
la integrazione diretta con i loro servizi di vendita (ricerca, pricing e prenotazione). Le
compagnie assicurano l’accesso a questi servizi tramite applicazioni web services.
Il modulo Spiderfly gestisce queste integrazioni. Si tratta di un applicativo web che espone
una interfaccia SOAP comune contattabile esclusivamente dal BSA.
I servizi esposti dalle compagnie non sono standard. Il modulo deve, assicurare il buon
funzionamento di ogni singola integrazione verso ogni singola compagnia. Normalizzando
a sua volta richieste e risposte tra standard con cui dialoga col BSA e quelli con cui dialoga
con la compagnia.
In fase di ricerca, il modulo stabilisce tramite determinate logiche, quali sono le compagnie
presso le quali effettuare la ricerca di orari e tariffe. Una volta individuate le compagnie, per
ciascuna, vengono interrogati i servizi necessari al fine di avere un elenco di soluzioni
vendibili. Tali soluzioni vengono combinate in una struttura dati comune che viene poi
restituita al chiamante.
Gli altri servizi esposti sono conferma tariffa e disponibilità e conferma della prenotazione.
Il meccanismo è simile a quello della ricerca. Anche in questo caso vengono invocati uno o
più servizi esposti dalla compagnia aerea per avere conferma della tariffa e per finalizzare
una prenotazione. La differenza più rilevante è che la compagnia, e quindi l’integrazione,
coinvolta è una sola e viene definita a partire dalla richiesta ricevuta in input. In questo caso,
non serve applicare logiche di business.
64
5 . L'impostazione del processo in Volagratis
Integrazione Sistemi GDS
Si tratta di un modulo web che espone anch’esso un’interfaccia web services, interrogata
dal modulo BSA.
Lo scopo di questo modulo è fornire al BSA una interfaccia unica per la ricerca, la conferma
di disponibilità e tariffe e la finalizzazione di una pratica di prenotazione che coinvolgono
voli forniti da sistemi GDS, con i quali questo modulo è integrato.
Lo scopo del modulo è lo stesso del modulo Spiderfly, ovvero fornire ai client
dell’applicativo (Volagratis e Webservices, tramite il modulo BSA) una interfaccia comune
per interagire con servizi che, dal punto di vista tecnico, nulla hanno in comune. Offrono si
servizi orientati alla ricerca e prenotazione voli, ma il modo con cui questi servizi devono
essere interrogato varia radicalmente in funzione del fornitore con cui ci si integra.
A differenza di Spiderfly, questo modulo non si integra direttamente con le compagnie
aeree, ma con i servizi GDS.
TO-BE: cosa vogliamo ottenere
Tante risorse sono state investite per potenziare il reparto IT interno all’azienda attraverso
nuove assunzioni e, soprattutto, attraverso una riorganizzazione Agile dei processi.
Tutti i team di sviluppo ora sono organizzati in modo Agile e lo sviluppo software avviene
con metodologia Scrum. I rilasci sono diventati frequenti ed incrementali, questo agevola
sia l’operazione di rilascio di quanto sviluppato (poche modifiche = rischi contenuti) che
quella di rollback alla versione precedentemente installata (poche modifiche = differenze
non evidenti).
Gli applicativi ed i moduli che attualmente costituiscono il sistema, soprattutto quelli più
datati, sono ancora abbastanza monolitici (legacy). La loro codebase è vasta, poco
refattorizzata e poco coperta da test.
A parte alcuni moduli, che sono gestiti da uno o comunque da pochi team, la maggior parte
degli applicativi e dei moduli ha una codebase soggetta a modifiche ed integrazioni da parte
di troppe persone, essendo tante le funzionalità deputate ad un singolo modulo. Questo
rischia di causare l’introduzione di bug o malfunzionamenti.
Quel che si vuole ottenere è quindi una architettura estremamente modulare. Le
responsabilità, ora ricadenti su uno o pochi moduli, devono essere suddivise in più moduli
applicativi più semplici. Ciascuno di questi applicativi ricade poi sotto la responsabilità di
pochi sviluppatori, uno o due team al massimo.
Questi moduli dovranno poi interagire tra loro. Ogni team curerà in autonomia gli sviluppi
necessari secondo quelle che sono le esigenze e le richieste del business e sarà responsabile
della loro messa on line secondo un calendario definito. L’importante è che non venga
65
5 . L'impostazione del processo in Volagratis
modificato il contratto tra moduli. Qualora questo avvenga, è compito dei membri dei team
coinvolti coordinarsi tra loro al fine di evitare che queste modifiche al contratto causino
problemi.
I passi di impostazione
Uno dei punti chiave di questa “metamorfosi” è l’adozione di metodologie in linea con i
principi del Continuous Development. L’obiettivo è ridurre le attività connesse al rilascio a
poche semplici operazioni, implementando una strategia di Continuous Delivery.
Come abbiamo visto, la Continuous Delivery è una estensione della Continuous Integration.
E’ da quest’ultima che bisogna cominciare.
Una strategia di Continuous Integration, è già in parte presente. Le mainline di tutti i moduli
sono poste in repository comuni e tenute sotto controllo da un’applicazione che, ad ogni
variazione del repository, lancia un processo di build e verifica che vada a buon fine.
Per tutti i moduli, l’esecuzione di test automatici fa parte del processo di build. Test che
però, al momento, sono presenti in misura ridotta rispetto alle dimensioni della codebase dei
moduli.
Un primo passo per potenziare la Continuous Integration già presente, è quello di aggiungere
test unitari e test applicativi (end2end). I primi vanno svolti automaticamente durante ogni
build, l’esecuzione dei secondi, invece, dipende dalla loro durata. Se non rallentano troppo
la build, possono farne parte, altrimenti devono essere schedulati a parte. Il fallimento di
questi test comporta il fallimento della build stessa.
In ottica DevOps, i test applicativi vengono definiti da sviluppatori e team QA (tester) in
collaborazione. Una volta definiti, è compito degli sviluppatori implementarli ed aggiungerli
alla strategia di Continuous Integration.
Una volta perfezionata la Continuous Integration, può essere estesa a Continuous Delivery.
Per fare ciò deve essere posto sotto versionamento, al pari del codice sorgente, tutto ciò che
riguarda il rilascio (ad esempio le directory del server web) e devono essere predisposti degli
script che, una volta mandati in esecuzione, procedono al rilascio dell’applicativo web del
modulo interessato, prodotto dalla build della Continuous Integration. Il rilascio può essere
sia su server di produzione, che su server di sviluppo o di collaudo.
Per predisporre questi script, sempre in ottica DevOps, è raccomandata la collaborazione tra
sviluppatori e sistemisti.
Una volta completata la modularizzazione del sistema ed implementata una strategia di
Continuous Delivery, ogni team potrà lavorare in autonomia ai propri moduli e potrà
concentrare la maggior parte delle proprie energie nello sviluppo di nuove funzionalità
richieste dal Business.
66
5 . L'impostazione del processo in Volagratis
La preoccupazione maggiore, in fase di rilascio, sarà quella di verificare il corretto
comportamento delle nuove funzionalità introdotte. Il rischio di regressione è scongiurato
dai test automatici introdotti e le operazioni tecniche legate al rilascio saranno ridotte a poco
più di una “formalità”.
67
5 . L'impostazione del processo in Volagratis
68
6. Attività operativa
6. Attività operativa
Veniamo ora all’attività operativa vera e propria. L’attività rappresenta il culmine di
un’esperienza svolta da due anni a questa parte all’interno di un team di sviluppo software
composto da 8 persone. Parallelamente all’attività di sviluppo e manutenzione del software
prodotto, il team ha affrontato un percorso di crescita e formazione che ha portato, come
stabilito dalle policies aziendali in tema Ricerca e Sviluppo, all’adozione di metodologie
Agili quali Extreme Programming e Scrum. A questo punto del percorso, il culmine è
rappresentato dall’adozione di una strategia di Continuous Delivery. Questa attività
rappresenta è stata svolta in parallelo alla ordinaria attività di sviluppo e manutenzione del
software dei moduli che compongono Volagratis. Infatti, se da un lato il management
aziendale favorisce ed incoraggia ogni tipo di “esperimento” volto a portare possibili
benefici futuri, purché svolto in linea con le decisioni aziendali, dall’altro richiede
giustamente che il livello di servizio offerto sia sempre allineato alle esigenze di mercato di
un’azienda con forti aspettative di crescita. Pertanto non è stato possibile interrompere
l’attività ordinaria di sviluppo e dedicarsi solamente alla “ricerca”.
Si è partiti da un modulo legacy, apportandovi le modifiche necessarie, e sviluppando una
pipeline che gestisse un processo di Continuous Delivery del modulo stesso. L’obiettivo
della attività operativa è stato quello di mettersi in condizione di deployare, tramite questa
pipeline, il modulo sul server di preview, lanciando un semplice comando (push button
deploy) specificando la versione da rilasciare.
Dal momento che la struttura del server di preview rispecchia in toto quella dei server di
produzione, il buon funzionamento di questa strategia permetterà in futuro di estendere
questo deploy a comando anche sui server di produzione.
Scelta del modulo candidato
Diversi fattori sono stati presi in considerazione per scegliere il modulo più adatto allo
scopo.
Sarebbe stato un paradosso parlare di “Continuous Delivery” su moduli che subiscono
modifiche poco frequenti così come sarebbe stato rischioso intervenire su un modulo che
viene modificato ad ogni sprint. Ecco quindi la necessità di trovarne uno soggetto a
modifiche con una certa regolarità.
Altro fattore considerato è stato il numero di team e di programmatori coinvolti nello
sviluppo di un modulo. Un alto numero di persone coinvolte implica difficoltà di gestione
dell’attività. L’implementazione del Continuous Development rischia di ostacolare l’attività
ordinaria sul modulo e viceversa.
Queste considerazioni hanno portato alla scelta di Spiderfly quale modulo candidato.
Risponde perfettamente alla considerazioni sopra esposte. Viene modificato ed aggiornato
69
6. Attività operativa
all’incirca ogni 40-60 giorni, è di competenza esclusiva di un solo team co-locato ed è sì
importante, ma un suo eventuale blocco non implica lo spegnimento, neppure temporaneo,
del sito Volagratis.
Tra gli aspetti negativi considerati durante la scelta del modulo, citiamo il fatto che è uno
tra i moduli più longevi di tutto il sistema. Negli anni ha subito pochi refactoring, è un
modulo legacy.
Scelta dei test e motivazioni
Per poter raggiungere l’obiettivo della Continuous Delivery, è necessario prevedere precise
strategie di test sul modulo, al fine di garantirne il corretto comportamento e ridurre il rischio
di introdurre involontariamente bug.
Agile ed Extreme Programming offrono una gamma di tipologie di test atte allo scopo. Ne
presentiamo un estratto, specificando lo scopo di tali test. Vedremo come questi test sono
stati coinvolti nell’attività e come non siano mancate le difficoltà che, talvolta, hanno
richiesto compromessi.
Test Unitari
I Test Unitari (Unit Test) sono test il cui scopo è quello di verificare il corretto
funzionamento di piccole porzioni del sistema, una classe od un gruppo ristretto di classi
legate tra loro. Questi test vengono eseguiti automaticamente, impiegando appositi metodi
e framework. Di norma il codice dei test unitari fa parte della codebase del progetto a cui
sono riferiti. E’ pratica raccomandata renderli parte del processo di build.
Una delle caratteristiche di questi test è che eventuali collaboratori esterni (altri moduli,
servizi esterni, ...) non dovrebbero essere invocati direttamente. Occorre, nei limiti del
possibile, sviluppare degli oggetti, noti come test-double (esempio stub, mock,...) che
simulino il comportamento di tali collaboratori, rendendo quindi questi test indipendenti
dallo stato del collaboratore esterno.
70
6. Attività operativa
Figura 27 Esempio di (semplice) classe (sopra) e della corrispondente classe con test unitario (sotto)
Il codice legacy del modulo Spiderfly è caratterizzato per la maggior parte da classi con alto
numero di metodi e righe di codice scritto, con molte responsabilità eterogenee, ricorrenti
ad un elevato numero di collaboratori. Sono pochi le classi ed i metodi coperti da test unitari.
Alla luce di questa analisi fatta sul codice, è emerso che fornire test unitari a tutto il codice
non ancora coperto avrebbe richiesto una mole di lavoro non trascurabile. Senza dimenticare
il fatto che si tratta di classi legacy, che necessitano di refactoring al fine di renderle più
comprensibili e più facili da manutenere, in linea con quelli che sono i principi Agili e le
nuove disposizioni aziendali.
Si è giunti quindi alla conclusione che predisporre test unitari adeguati sarebbe stato molto
oneroso e avrebbe sottratto troppo tempo a quelle che sono le normali attività di sviluppo
del team. Senza dimenticare che il codice legacy sarà a breve oggetto di refactoring. Si è
deciso quindi di derogare da quelli che sono i principi canonici dei test unitari, scrivendo
classi e metodi che svolgano, per ogni compagnia con la quale il modulo si integra, test del
flusso completo di ricerca e prenotazione voli, coprendo varie casistiche quali, ad esempio,
prenotazione standard, prenotazione con neonati, prenotazione con bagagli, e così via. Le
chiamate ai fornitori esterni ed agli altri moduli da cui Spiderfly dipende, vengono simulate,
in modo che i test non vengano influenzati dallo stato del collaboratore esterno. Lo scopo di
questi test, infatti, è quello di verificare il comportamento delle classi del modulo in presenza
di quelle che sono le risposte attese ricevute dai servizi esterni. Le classi che li compongono
fanno parte della codebase del progetto.
Anche se non si tratta di test unitari veri e propri, il loro esito positivo è sufficiente a garantire
il corretto comportamento di tutte le componenti del modulo. La loro durata contenuta
(inferiore ai 10 secondi) ci ha fatto messo in condizione di inserire lo svolgimento di questi
test tra i vincoli per l’esito di ogni processo di build sia locale che sul server di Continuous
Integration.
71
6. Attività operativa
Si tratta chiaramente di una fase transitoria. A livello di team si è convenuto che:
i) tutti i nuovi sviluppi sul modulo dovranno essere costituiti da classi piccole, con poche
responsabilità omogenee, con il minimo numero di collaboratori e coperte da adeguate classi
di test unitari;
ii) sviluppi al codice esistente dovranno essere l’occasione per procedere a refactoring che
rispecchino quanto già detto al punto i;
iii) Si investirà parte del tempo in refactoring del codice esistente al fine di migliorarlo.
Smoke Test
Gli smoke test verificano che, una volta che l’applicazione è stata deployata, su un qualsiasi
server ad essa dedicato (sviluppo, collaudo, produzione, ...) sia correttamente in funzione.
Non è loro responsabilità verificare il corretto comportamento dell’applicazione.
Per fare questo, è stato predisposto uno script che si collega all’url a cui l’applicazione web
risponde e verifica che nel codice html della pagina sia presente una determinata stringa.
Test End2End
I test End2End verificano il corretto comportamento di una, alcune, o tutte le funzionalità
offerte dall’applicazione, senza verificarne la struttura interna. Ad esempio, verificano che
una determinata funzionalità risponda con l’output atteso ad un determinato input, oppure,
in ambito web, che durante una interazione con l’applicazione web che prevede la
navigazione tra pagine, questa segua il flusso corretto.
Se eseguiti verso un’applicazione web, questa deve essere in esecuzione su di un server web.
L’applicazione o gli script che eseguono i test, la contattano ed interagiscono con essa. Nel
caso l’applicazione abbia forti dipendenze con servizi esterni, ed è il caso di Spiderfly, si
può decidere di simulare le chiamate a tali servizi. Non è obiettivo di questi test fallire in
caso di malfunzionamenti esterni. Figura 28 Batches o applicazioni per smoke o end2end test che interagiscono con l’applicazione da
testare
72
6. Attività operativa
I test descritti nel paragrafo dedicato ai test unitari, sviluppati per ovviare a delle difficoltà,
possono essere classificati come test end2end, anche se “impropriamente” in quanto non
costituiscono un progetto a se stante e vengono eseguiti come test unitari.
Quando, tra refactoring e nuovi sviluppi, la codebase del progetto avrà una copertura
accettabile di test unitari, gli attuali test “ibridi” potranno essere esportati in un’applicazione
a se stante e gestiti come test end2end a tutti gli effetti.
Integration Test
Verificano che l’applicazione funzioni secondo le aspettative. Questi test non prevedono
simulazioni di chiamate verso servizi esterni, che pertanto devono essere effettuate da parte
dell’applicazione testata.
Nell’ambito dell’applicazione web Spiderfly, vista la buona affidabilità dei test
Unit/End2End, che garantiscono il buon funzionamento di tutte le componenti del modulo,
si è deciso di lasciare che questi test vengano svolti in modo manuale sulle funzionalità
aggiunte o modificate. E’ compito di chi le sviluppa predisporre dei casi di test e svolgerli
in prima persona. Questi verranno poi validati da altri membri del team e/o dal Product
Owner del modulo, svolgendo i medesimi test su una macchina di sviluppo, che può essere
sia la propria workstation, sia il server di sviluppo a disposizione del team. E’ consigliato
coinvolgere anche gli Stakeholders in questi test. Scelta degli strumenti
L’attività è stata condotta utilizzando diversi strumenti e framework atti allo scopo. Alcuni
di questi erano già in uso in azienda da anni, mentre altri sono stati adottati per l’occasione,
selezionati tra quelli proposti da un gruppo aziendale di esperti (“Tech Leaders”) che ha il
compito di individuare quali strumenti sono impiegabili a livello aziendale.
Subversion
Subversion è un sistema di controllo di versione per file e directory. E’ utilizzato in azienda
per gestire il versionamento dei sorgenti e degli script SQL necessari per modificare i
database al servizio delle varie applicazioni. In ottica Continuous Development è stato
impiegato per versionare e centralizzare i server web su cui le applicazioni devono essere
deployate. Con opportune distinzioni tra server per macchine di sviluppo, collaudo o
produzione.
73
6. Attività operativa
Maven
Maven è un potente strumento impiegato nella gestione dei progetti software e del loro
processo di build. E’ usato principalmente per la gestione di progetti Java, può essere usato
anche per gestire progetti scritti in altri linguaggi. Il file di configurazione del progetto è il
Project Object Model (pom.xml). Tramite questo file, è possibile, tra le altre cose, definire
le librerie, da cui un progetto dipende. E’ compito di Maven scaricare tali librerie da
repository che possono essere quelli pubblici di default, oppure anche custom, e renderle
disponibili durante i processi di compilazione e di build.
Figura 29 Esempio estratto da pom.xml di un progetto Maven. Vengono definiti il packaging del
progetto e le dipendenze
74
6. Attività operativa
Jenkins
Jenkins è un’applicazione web che, sostanzialmente, permette l’esecuzione di task a
scadenze schedulate. Tra i task eseguibili vi è anche, e soprattutto, l’esecuzione di processi
di build e test di progetti software. Jenkins è quindi un ideale strumento di coordinamento
per implementare strategie Continuous Development.
Figura 30 Esempio console Jenkins
Inizialmente è stato impiegato a livello aziendale per verificare che le modifiche alla
codebase dei vari moduli, committate sul VCS (Subversion) non pregiudicassero il corretto
esito della build del modulo. A scadenze regolari, e configurabili, Jenkins verifica se sono
stati fatti commit sulla codebase del progetto. In tal caso, scarica la codebase del modulo
coinvolto dal server Subversion ed eseguire il processo di build, che include anche i test
unitari.
Jenkins permette di eseguire task in cascata ed anche, come task pianificati, script bash più
o meno semplici sia localmente sul server ove l’applicazione Jenkins è installata, sia in
remoto sfruttando, ad esempio, il protocollo ssh. Così come permette di eseguire task in
cascata.
Questo ci ha permesso, in ottica Spiderfly, di implementare una pipeline che rispetti le best
practices suggerite dal Continuous Development.
75
6. Attività operativa
JUnit
JUnit è un semplice framework che permette di scrivere test automatici ripetibili. E’ adottato
a livello aziendale per la scrittura e l’esecuzione dei test unitari inseriti in ogni modulo e
svolti a tempo di build. Si tratta del framework con cui vengono scritti e svolti i (pochi) test
unitari già presenti nel modulo.
Cucumber
Si tratta di un framework a supporto delle metodologie BDD (Behaviour Driven
Development). Il BDD è una metodologia rientrante sempre nella galassia Agile, affine al
già esposto TDD. Prevede lo sviluppo di software a partire dalla definizione testuale di
quello che è il comportamento atteso da parte del codice testato (file feature). Queste
definizioni sono mappate in classi e metodi di test, che possono essere scritti in diversi
linguaggi di programmazione, e che svolgono materialmente il test. Quando tutte le
aspettative sono soddisfatte, il software è sviluppato correttamente. Una caratteristica dei
metodi di test è che possono essere riusati in più occasioni in scenari di test diversi.
Figura 31 Esempio di scenario di test per programma che effettua divisioni
Figura 32 Mapping in Java dello scenario di cui sopra. I metodi accettano un parametro in input,
quindi possono essere usati anche in scenari differenti.
76
6. Attività operativa
Figura 33 Output prodotto dal run dello scenario di test
Con un leggero abuso, Cucumber è stato sfruttato per la scrittura dei test del modulo
Spiderfly che testano i flussi di ricerca e prenotazione voli. Questa scelta è stata dettata dalla
possibilità, offerta da Cucumber e dal BDD, di comporre i test utilizzando più volte i metodi
scritti, nell’ambito di più casi di test.
Mockito
Per la sua natura e le sue caratteristiche, Spiderfly ha diverse dipendenze esterne, almeno
una per ogni compagnia aerea con la quale è integrato. Mockito è una libreria che permette
di simulare il comportamento di librerie e servizi esterni. Ogni volta che viene fatta una
chiamata a questi, Mockito, opportunamente impiegato e configurato nel codice delle classi
di test, si frappone tra il chiamante ed il chiamato e fornisce una risposta predefinita in fase
di configurazione. La risposta può essere standard uguale per tutte le chiamate o variare in
funzione di queste.
Chiariamo con un esempio.
Figura 34 Esempio test con Mok
Questa classe deve testare il comportamento della classe MapDrawingService, la quale
ha FlightTrackingRepository quale collaboratore mockato. Infatti tale oggetto è
77
6. Attività operativa
annotato @Mock ed è iniettato nell’oggetto mapDrawingService (notazione @InjectMocks). In questo modo, il collaboratore non dovrà mai entrare in azione,
facendo chiamate esterne ad esempio, ma dovrà fornire risposte predefinite qualora venga
invocato.
Il comportamento predefinito viene così configurato:
Figura 35 Configurazione oggetti Mock
Questo frammento di codice stabilisce che quando viene invocato il metodo getWayPoints dell’oggetto mockato, con parametro “testId”, il collaboratore dovrà
restituire la lista costruita appena sopra.
Ansible
Ansible è un semplice tool di automazione IT. Può essere utilizzato per configurare sistemi,
installarvi applicazioni, fino ad una completa gestione di task complessi quali, ad esempio,
il Continuous Deployment.
Nell’ambito della nostra attività operativa è stato impiegato solamente per mantenere
allineati i vari ambienti per quel che riguarda il software installato (Jdk, Web Server, ...) e
l’alberatura delle directory.
78
6. Attività operativa
Il processo nella pratica
Una volta selezionati gli strumenti, vediamo come questi sono stati impiegati per arrivare
ad avere una strategia di Continuous Development implementata sul modulo Spiderfly.
Figura 36 Visione d’insieme del processo (icone Jenkins e Tomcat tratte dai rispettivi siti web)
Tomcat e Subversion
Tutte le istanze dell’applicazione Spiderfly, vale a dire sia quelle sulle workstation degli
sviluppatori che quelle sulle macchine di collaudo, preview e produzione devono essere
deployate e girare su di un server web (container Tomcat nel nostro caso) con le medesime
caratteristiche. Sono ammesse solamente alcune differenze a livello di configurazione quali,
ad esempio, una diversa verbosità del logging. Per raggiungere questo scopo, sono state
sfruttate le potenzialità offerte da Subversion. Sono state create e versionate le directory,
una per ogni tipologia di server (sviluppo, collaudo, preview e produzione) contenenti tutto
il necessario al funzionamento di Tomcat (librerie, script di start e stop, link simbolici, ...).
E’ responsabilità degli sviluppatori assicurarsi di avere il proprio Tomcat locale sempre
allineato con quanto posto sotto versionamento, e committare eventuali modifiche fatte alla
79
6. Attività operativa
configurazione del server, con la stessa cura con cui viene gestito il versionamento dei
sorgenti.
A livello di allineamento delle macchine di collaudo, preview e produzione, invece, lo script
che gestirà il deploy dell’applicazione web dovrà preventivamente allineare la directory del
server Tomcat a quanto presente sotto Subversion.
Processo di Build
Il progetto Spiderfly prevedeva la presenza di profili di build. Nel momento in cui veniva
lanciata una build tramite Maven, da un utente sulla propria workstation, oppure
dall’applicazione Jenkins, era possibile e necessario specificare un profilo Maven col quale
questa dovesse essere eseguita. In questo modo durante il processo di local build, Maven
procedeva opportunamente all’inclusione o esclusione di vari file, soprattutto di
configurazione, dal package finale a seconda di quello che era il profilo impostato, correlato
alla destinazione del package creato (produzione, collaudo, test in sviluppo,...). Da un punto
di vista pratico questo era molto comodo, ma violava il principio secondo il quale il package
war deve essere unico, indipendentemente dalla sua destinazione d’uso. Questo allo scopo
di garantire a chi ha il compito di testare l’applicazione, che il software che stà testando è il
più possibile uguale a quello che si avrebbe in produzione qualora quello stesso package
venisse installato su tale server.
Il processo di build è stato quindi centralizzato sull’applicazione Jenkins, che crea il package
e lo mette a disposizione su di un repository. Nel momento in cui il package dovrà essere
installato su di un server, sarà compito del server prelevarlo dal repository. Il package creato
contiene e mette a disposizione tutti i file di configurazione sopra descritti, che in precedenza
venivano inclusi o esclusi durante il processo di build del package. Il compito di utilizzare
quelli corretti in funzione dell’ambiente in cui ci si trova è demandato al framework Spring
durante l’inizializzazione dei contesti. Leggendo una proprietà del server web, legge i file
corretti durante l’inizializzazione dell’applicazione.
Vediamo un esempio chiarificatore, abbiamo più directory contenenti i file di
configurazione
Figura 37 Directory contenenti file di configurazione
Nello script di avvio del server, viene definita una proprietà che specifica con che profilo
caricare l’applicazione
Figura 38 Definizione property nel file di avvio di Tomcat
80
6. Attività operativa
Il file di configurazione dei contesti Spring, letto in fase di caricamento dell’applicazione,
crea il bean environmentPrefix, che può essere inizializzato con valori differenti, a
seconda del profilo.
Figura 39 Estratto dal file di caricamento dei contesti Spring
Dato che, nel file di configurazione del server, il profilo definito è “local”, Spring
inizializza il bean environmentPrefix, con il valore “/development/”. Nel codice
dell’applicazione, ogni volta che occorre leggere un file di configurazione, si può e si deve
risalire al path corretto del file di configurazione utilizzando il valore con cui è stato
valorizzato environmentPrefix. Questo è un aspetto molto delicato, l’uso di un file sbagliato rischia di compromettere
seriamente il buon funzionamento dell’applicazione. E’ un rischio che, forti del fatto che
anche i file di configurazione dei server web sono gestiti tramite VCS, abbiamo deciso di
correre.
Test con Mockito e Cucumber
Come specificato in precedenza, fornire al progetto una adeguata copertura di test unitari
avrebbe richiesto la scrittura ed il refactoring di molte righe di codice. Attività che non
poteva essere inclusa tra quelle svolte dal team. Si è quindi deciso di implementare, per ogni
compagnia aerea con la quale il modulo è integrato, dei test che verifichino il corretto
funzionamento del flusso completo dell’integrazione, dalla ricerca alla finalizzazione della
prenotazione. Per descriverli semplicemente, si tratta di test end2end svolti al posto dei test
unitari, vale a dire subito dopo la compilazione del codice sorgente.
Durante la scrittura di questi test si sono affrontate due problematiche importanti: la forte
dipendenza tra il modulo e sistemi esterni (i sistemi di ricerca e prenotazione delle varie
compagnie aeree) e l’alto numero di casistiche da coprire con test, quali, ad esempio ricerca
e prenotazione volo di sola andata, volo di andata e ritorno, con neonati, con tariffe
particolari, con o senza acquisto del bagaglio, e così via.
81
6. Attività operativa
La prima problematica è particolarmente insidiosa per tre motivi:
● Nulla si sa circa l’effettiva disponibilità del sistema esterno nel momento in cui
vengono eseguiti i test
● Disponibilità e tariffe variano in continuazione, mentre i test verificano sempre il
corretto funzionamento di scenari predefiniti: ad esempio “Il volo Vueling da
Malpensa a Barcellona del 01/08/2015 alle ore 08:50 costa 50 euro tasse incluse”.
E’ impossibile che il sistema Vueling, in questo caso, ci dia sempre una risposta che
soddisfi questa asserzione. Soprattutto a cavallo, o dopo il 01/08/2015.
● Annullare prenotazioni fatte a scopo di test richiede sempre l’intervento di un
operatore. Fattibile quando le prenotazioni sono nell’ordine della decina a settimana,
impensabile quando sono nell’ordine di centinaia al giorno.
Per questi motivi, si è ricorso a strumenti che simulino la chiamata verso l’esterno, in
particolare si è fatto largo uso della libreria Mockito. Ciò ha permesso di modificare il
comportamento di oggetti che, in realtà, effettuerebbero chiamate verso sistemi esterni,
facendo in modo che restituiscano risposte predefinite secondo le esigenze di test.
Un esempio di mock impiegati durante i test è riportato in questa figura Figura 40 Esempio dichiarazione e iniezione mock per chiamate verso sistemi esterni
La classe WSBookingClient contiene dei collaboratori che effettuano chiamate verso
sistemi esterni. In occasione dei test, vengono gestiti da Mockito (wsIGPaymentSoap e wsIGRetrieveTicketSoap sono annotati @Mock) che sono configurati per fornire
output standard in funzione degli input ricevuti.
In questo caso, è possibile testare il comportamento del modulo, senza preoccuparsi della
risposta del sistema esterno. Il fallimento di uno o più scenari di test indicherebbe che è stata
fatta una modifica al modulo che ne ha alterato il comportamento. Le cause vanno quindi
cercate esclusivamente all’interno del codice del modulo. Una volta individuate, si procede
alla sistemazione del problema, in modo che i test ricomincino ad avere esito positivo nel
più breve tempo possibile. E’ importante specificare che, non sempre, un fallimento di uno
o più test richiedono fix sull’applicazione. Potrebbe essere che le modifiche fatte fossero
necessarie per svariati motivi. In tal caso, occorre valutare se i test falliti sono ancora
necessari e, in tal caso, allinearli al nuovo comportamento del modulo.
82
6. Attività operativa
La seconda problematica pone il focus sulla quantità di righe di codice da predisporre (e
quindi da manutenere) per testare scenari con molti punti in comune.
Per far fronte a questo, si è impiegato il framework Cucumber per la predidposizione delle
classi di test. Partendo da file testuali con una sintassi relativamente semplice, sono stati
predisposti vari scenari di test. Gli scenari di test sono stati mappati su opportuni metodi e
classi a da parte del framework Cucumber, tramite un plugin installato nell’IDE.
Chiariamo il funzionamento di Cucumber con un semplice esempio.
La figura sotto mostra un template (Scenario Outline) inserito in un file feature Cucumber.
Le descrizioni dello scenario sono parametrizzate ed è presente una tabella (Examples).
Ogni riga di questa tabella rappresenta un set di parametri per lo scenario. Quanto riportato
nell’esempio sotto, quindi, specifica due casi di test del servizio del modulo Spiderfly che
si occupa di creazione di una pratica di prenotazione per una determinata compagnia aerea.
Lo scenario è stato mappato poi in classi e metodi di test. Notare anche la relativa semplicità
con la quale possono essere introdotti ulteriori casi di test.
Figura 41 Scenario Cucumber per test flusso creazione pratica prenotazione di una compagnia aerea
Scenari di questo tipo sono stati creati per ogni servizio offerto da Spiderfly e per ogni
compagnia aerea con cui il modulo è integrato. La situazione finale vede più Scenario
Outline parametrizzati che condividono dove possibile gli stessi metodi (parametrizzati) su
cui sono mappati. Abbiamo pertanto molti scenari a fronte di un numero accettabile di classi
e metodi di test da manutenere.
Oltre alla possibilità di riusare quanto più possibile il codice predisposto per i test, il fatto
che Cucumber parta da file scritti in linguaggio naturale con poche regole sintattiche offre
la possibilità a chiunque legga tali file di capire cosa venga testato dai vari scenari.
Script di deploy su server di sviluppo
Sul server di sviluppo a disposizione del team, è stato predisposto uno script che: i) ferma il
web server e lo aggiorna a quanto presente sotto Subversion; ii) scarica dal repository
comune il package war predisposto dall’applicazione Jenkins e lo installa sul web server;
iii) riavvia il web server.
83
6. Attività operativa
Essendo il progetto Spiderfly gestito tramite Maven, il package war è caratterizzato da un
numero di versione. Che deve essere specificato nell’input dello script, affinché questo
scarichi ed installi la versione corretta.
Lo script può essere lanciato manualmente da un utente collegato al server di sviluppo,
oppure da Jenkins, sfruttando una connessione tra l’applicazione ed il server di sviluppo.
Inoltre, lo script è eseguibile sulle workstation di sviluppatori e tester, a patto che
l’alberatura delle directory contenenti il server web rispetti gli standard definiti a livello
aziendale. In questo modo chiunque è in grado di scaricare e installare localmente una
determinata versione dell’applicazione.
Di fatto questo script offre la possibilità di eseguire il “push-button-deploy” su di una
qualsiasi macchina. Dal momento che l’architettura e la struttura delle directory del server
di sviluppo è uguale a quella dei server di collaudo, preview e produzione, è sufficiente
esportare ed eseguire questo script su tali server per installarvi l’applicazione in maniera
semplice e rapida.
Jenkins Pipeline
Sfruttando le potenzialità offerte dall’applicazione Jenkins, sono stati predisposti una serie
di task eseguiti in cascata, volti ad implementare una pipeline così strutturata:
● Build, ovvero compilazione, test unitari e creazione del package war
dell’applicazione Spiderfly
● Invocazione dello script di deploy sul server di sviluppo di cui al paragrafo
precedente.
● Esecuzione di un semplice smoke test. Si tratta di uno script gestito dall’applicazione
Jenkins che chiama la url della home page dell’applicazione appena installata sul
server di sviluppo e ricerca una stringa nell’html della pagina. La presenza di questa
stringa significa che la applicazione web è correttamente up and running sul server
di sviluppo.
Questi task possono essere lanciati singolarmente a scopo di debug o di verifiche parziali.
L’esecuzione della pipeline è configurata in modo da partire ogni volta che un utente esegue
un commit sulla codebase del progetto. Oltre a questo, i task che compongono la pipeline
possono essere lanciati, in toto o in parte, da un qualunque utente connesso alla consolle
Jenkins. Il fallimento di un singolo task comporta il fallimento dell’intera pipeline con
immediata notifica dell’avvenimento al team di sviluppo. In questo caso, la risoluzione del
problema, con eventuale rollback delle modifiche committate, assume la massima priorità.
Viceversa, una corretta esecuzione della pipeline garantisce che una eventuale installazione
in produzione del codice al momento sotto VCS, non comporterebbe problemi. I test unitari
eseguiti dal primo task garantiscono il corretto comportamento dell’applicazione, mentre gli
smoke test del terzo task garantiscono che l’applicazione viene correttamente installata sul
web server.
84
6. Attività operativa
Questo il diagramma che illustra il flusso della Pipeline sviluppata: Figura 42 Diagramma di flusso della pipeline
E’ bene ricordare che a livello di team è stato stabilito che le nuove funzionalità introdotte
o le modifiche fatte a quelle esistenti, oltre ad essere coperte da test unitari adeguati, devono
essere testate manualmente prima della loro messa in produzione, sfruttando l’applicazione
installata sul server di sviluppo, oppure installando la versione dell’applicazione che
contiene le modifiche da testare, su una workstation locale.
85
6. Attività operativa
Qualora si intenda demandare a Jenkins il deploy anche su altri server, sarà sufficiente
portare lo script di deploy su tali server, come descritto precedentemente e predisporre un
task Jenkins, ad esecuzione manuale o condizionata, che invochi tale script.
La console Jenkins per il progetto Spiderfly, che si occupa di lanciare la build della
codebase, usando Maven, appare così: Figura 43 Console Jenkins progetto Spiderfly
Ed ha in downstream il progetto “GIANLUCA_Spiderfly”, che è un altro task Jenkins, così
configurato
Figura 44 Configurazione progetto in downstream
Questo progetto a sua volta chiama in sequenza altri due task Jenkins, uno che invoca via
ssh lo script di deploy sul server di sviluppo ( che si chiama Gianluca) e l’altro che esegue
uno smoke test. Notare che il primo script accetta in input un parametro “VERSION”, che
è un parametro definito nella configurazione del progetto “GIANLUCA_Spiderfly”.
Quando la build del progetto spiderfly2 termina senza errori, abbiamo questo output in
consolle, che comunica che è stato lanciato un processo in downstream
Figura 45 Triggering di un processo in downstream
86
6. Attività operativa
Il cui output si presenta così
Figura 46 Output processo di rilascio e smoke test
Il rilascio e lo smoke test hanno avuto esito positivo, l’applicazione appena buildata è on
line sul server di sviluppo: Figura 47 Applicazione aggiornata up and running
Non tutto automatizzato
Non tutti i processi legati al rilascio dell’applicazione su di un server, in qualunque
ambiente, sono stati automatizzati. Alcuni continuano e continueranno ad essere svolti
manualmente.
Viene mantenuta la politica di code-freeze. Al termine di uno sprint Scrum, quando una
determinata versione dell’applicazione (X.Y-SNAPSHOT secondo le convenzioni Maven),
supera i controlli qualitativi, i commit sulla codebase vengono temporaneamente bloccati,
si procede al tag di questa versione ed alla creazione e messa in preview prima e produzione
poi, del relativo pacchetto war.
Anche le modifiche al database al servizio dell’applicazione rimangono a gestione manuale.
Ogni volta che vengono fatti sviluppi che implicano una modifica, di qualunque tipo, al
database, è compito di chi predispone tali sviluppi predisporre e lanciare gli script che
modificano il database in ambiente di sviluppo ed assicurarsi che tali modifiche non abbiano
87
6. Attività operativa
prodotto effetti collaterali. Allo stesso modo questi script devono essere condivisi via
Subversion, è sempre compito di chi li predispone, definire quando questi debbano essere
lanciati anche negli ambienti di collaudo e produzione ed assicurarsi che vengano eseguiti
da personale preposto e che non producano effetti collaterali.
Restando in tema di gestione manuale, rimane tale anche la gestione sistemistica dei server.
Gli aggiornamenti di sistema operativo, versione Java, e simili, vengono lanciati
manualmente con l’ausilio, ove possibile, di Ansible, che assicura l’uniformità su tutti i
server, per quanto riguarda il software necessario all’esecuzione dell’applicazione web
Spiderfly, e l’alberatura delle directory.
Obiettivi raggiunti
L’implementazione del processo di Continuous Development sul modulo Spiderfly, che è
culminata con l’introduzione di una Pipeline, ha portato al raggiungimento di obiettivi che
hanno sicuramente contribuito a migliorare la qualità del lavoro svolto dall’intero team.
Vediamo in dettaglio i benefici apportati dall’attività svolta.
Feedback Rapido
L’introduzione della pipeline, e soprattutto il fatto che questa venga eseguita ogni volta che
la codebase presente sul sistema VCS Subversion viene aggiornata, ha portato una serie di
vantaggi sia agli sviluppatori che ai Product Owner ed agli Stakeholders dei prodotti. I primi
possono avere feedback immediato ogni volta che committano qualcosa sulla codebase.
Feeback che può essere positivo, con le modifiche appena committate up and running sul
server di sviluppo, o negativo con notifica di un fallimento dell’esecuzione della pipeline, e
delle relative cause, in modo che possano essere analizzate e risolte al più presto.
I secondi, invece, possono accedere all’applicazione web installata ed aggiornata sul server
di sviluppo, vedere e verificare che gli sviluppi da essi richiesti e discussi col team rispettino
i criteri di accettazione e fornire feedback agli sviluppatori.
Deploy automatico
Oltre al feedback rapido appena descritto, la Pipeline assicura che l’applicazione web
installata sul server di sviluppo è sempre allineata al codice presente sul sistema Subversion.
Di fatto, nessuno sviluppatore deve più investire tempo ad installare l’applicazione sul
server di sviluppo, ed in ogni momento vi è chiarezza su quella che è la versione ivi
installata. L’unica cosa che gli sviluppatori devono fare, è conoscere lo stato della pipeline.
88
6. Attività operativa
Se è funzionante, possono dedicarsi ad altro. Se è, viceversa, non funzionante, devono
organizzarsi per riportarla ad uno stato funzionante.
Per situazioni anomale, quali ad esempio la necessità di sistemare un malfunzionamento
emerso su di una versione già installata in produzione, è possibile interrompere la pipeline
prima che questa effettui il deploy in sviluppo. Lo script di deploy automatico permette di
specificare la versione da installare e può quindi essere utilizzato anche in questi casi.
L’importante è che tutti coloro che utilizzano l’applicazione installata sul server di sviluppo,
siano informati del fatto che sul server vi è temporaneamente una versione differente rispetto
all’ultima.
Miglioramento efficienza test
Una delle attività svolte è stata l’introduzione dei test che verificano il corretto
funzionamento del flusso ricerca e prenotazione voli per ogni singola integrazione gestita
dal modulo. Per come sono stati sviluppati e strutturati, questi test forniscono garanzia circa
il corretto funzionamento del modulo in ogni componente, inteso come il fornire le risposte
attese a fronte di determinati input. Hanno così oviato ad una tra le più evidenti carenze
presenti nel modulo legacy prima che venisse svolta questa attività. A parte pochi test unitari
scritti e gestiti con JUnit, svolti su alcune funzionalità particolari, non vi erano nessun test e
nessuna combinazione di test che fornissero garanzie sul funzionamento dell’intero sistema.
Questi test scongiurano il rischio che venga, inavvertitamente, introdotta regressione nel
sistema o, qualora questo avvenga, permettono di scoprirlo tempestivamente ed aiutano ad
individuare il punto dove questa è stata introdotta.
War unico
L’abolizione del legame tra profilo con cui viene eseguita la build, ed output del processo
era da tempo nella “TODO-list” del team. Questa è stata l’occasione per procedere.
Ricordiamo che questo legame imponeva al processo di build l’inclusione o l’esclusione di
determinati file, soprattutto di configurazione, dal package war finale. In parole povere non
avevamo un war unico, bensì uno per ogni ambiente su cui l’applicazione può essere
installata (sviluppo, collaudo, preview, produzione).
Il war unico, oltre a rispettare le best practices della Continuous Delivery, elimina il rischio
che potrebbe derivare dalla messa in produzione di un war contenente file incompatibili con
l’ambiente di produzione quali, ad esempio, configurazioni interne che fanno puntare
l’applicazione ad ambienti non di produzione dei fornitori coi quali è integrata, con
conseguenze che potrebbero essere molto poco piacevoli.
89
6. Attività operativa
Difficoltà incontrate
Non è stato difficile arrivare ad avere una strategia di Continuous Delivery gestita da una
Pipeline, quanto essere confidenti che l’output di questo processo non mettesse on line,
seppure solo su un server ad uso interno, del software contenente malfunzionamenti o,
peggio ancora, regressione. Per raggiungere questa confidenza, è stato necessario introdurre
dei test.
L’introduzione dei test è stata molto difficoltosa. La struttura del modulo, essendo tra quelli
presenti da più tempo nell’architettura Volagratis, contiene molto codice legacy, soprattutto
classi sovraresponsabilizzate e/o con un numero elevato di dipendenze e di collaboratori.
Non era pertanto pensabile di introdurre test unitari su classi così complesse e non vi erano
i tempi e la disponibilità di sviluppatori per una ristrutturazione del codice. E’ stato anche
per questo motivo che la scelta è ricaduta sui test “ibridi” unitari/end2end descritti in
precedenza, la cui stesura è stata laboriosa a causa dell’alto numero di casi di test da
prevedere e di chiamate esterne da simulare presenti in ogni flusso.
Altro punto di difficoltà è stata la modifica al codice del modulo conseguente
all’eliminazione dei profili di build. Dal momento che l’applicazione è stata modificata in
modo che la sua installazione può contenere più versioni di uno stesso file, o di una stessa
libreria, si è resa necessaria una attenta verifica del codice volta ad individuare i punti in cui
questi vengono usati ed inserire opportune istruzioni che facessero usare il file o la libreria
corretti in funzione della configurazione del server su cui l’applicazione è in esecuzione,
prevedendo anche un comportamento difensivo di default nel caso non sia possibile stabilire
su che ambiente l’applicazione è in esecuzione.
90
7. Conclusioni
7. Conclusioni
Arrivare all’implementazione di una strategia di Continuous Delivery, è stato un processo
lungo e complesso, ma allo stesso tempo entusiasmante e stimolante dal punto di vista della
crescita professionale. Ha portato dei risultati positivi che sicuramente sono incoraggianti
qualora si voglia implementare una simile strategia anche su altri moduli.
L’adozione delle metodologie Agili nell’organizzazione del lavoro di sviluppo software ha
rappresentato sicuramente un buon punto di partenza per la buona riuscita di questo
progetto. Possiamo affermare che investire tempo e risorse nella formazione Agile e Scrum,
hanno dato i loro frutti, e continueranno a darli in futuro.
Il lavoro svolto
Per meglio analizzare il lavoro svolto ed apprezzare i benefici introdotti dall’adozione, a
livello aziendale, delle metodologia Agili Scrum ed Extreme Programming prima e della
strategia di Continuous Delivery poi, considerandoli nel loro insieme.
L’introduzione della lavorazione a sprint ha portato una innovazione nel modo con cui i cicli
di sviluppo ed i rilasci vengono gestiti. Il team, grazie ad un fitto scambio di informazioni
con Product Owner, Stakeholders e Business Owner ha modo di ben comprendere quelli che
sono i requisiti relativi agli sviluppi, li discute internamente in modo da poter fornire stime
sulla mole di lavoro necessaria per portarli a termine e crea le storie, ovvero i task che
verranno inseriti nell’elenco di lavorazioni da svolgere durante un determinato sprint
(backlog), secondo una priorità definita da Product e Business Owner.
A questa organizzazione del lavoro, l’introduzione della Pipeline ha portato un valore
aggiunto. Ed è quello di rendere immediatamente disponibili, a tutti gli utenti che hanno
accesso all’applicazione web installata sul server di sviluppo, le implementazioni fatte nel
corso dello sprint, committandole su server Subversion. In questo modo Stakeholders,
Product e Business Owner hanno modo di verificare che gli sviluppi fatti rispondano ai
requisiti e fornire un feedback continuo agli sviluppatori.
L’introduzione di una suite di test svolti durante il processo di build, ha fornito sicurezza
circa la stabilità del software sviluppato e, soprattutto, ridotto il rischio che in produzione
vengano introdotti bug o, peggio, regressione. Per poter mantenere questa sicurezza e,
perché no, aumentarla, è molto importante che tutti gli sviluppi futuri che verranno fatti
vengano accompagnati da test adeguati.
Anche i server di produzione sono stati interessati dall’attività. In ottica “push-button
deploy” sono stati predisposti script, simili a quello fatto per il server di sviluppo, che però
non vengono invocati da nessun processo automatico. Quando eseguiti manualmente: i)
allineano il web server a quanto presente sotto VCS Subversion; ii) scaricano ed installano
sul server il pacchetto predisposto dall’applicazione Jenkins, e quindi dalla Pipeline. Di fatto
91
7. Conclusioni
svolgono una serie di operazioni che, precedentemente, venivano eseguite manualmente da
personale addetto.
Vale la pena citare, tra il lavoro svolto, il primo deploy in produzione del pacchetto war
predisposto dalla pipeline senza l’uso dei profili Maven durante il processo di build. Il war,
e quindi l’applicazione, conteneva diverse versioni, alternative tra loro, di file di
configurazione molto importanti per l’applicazione. Stava poi alla configurazione del server
web definire le corrette versioni dei file da utilizzare. Si trattava quindi di una modifica
relativamente semplice dal punto di vista tecnico/architetturale, ma molto rischiosa per il
corretto funzionamento dell’applicazione. Ebbene, questa modifica è stata rilasciata su di
un server di produzione e possiamo dire che le prestazioni di questo server pilota sono state
assolutamente soddisfacenti ed in linea con quelle degli altri server con l’applicazione non
ancora aggiornata. Così come i log dell’applicazione non hanno riportato anomalie o errori
legati a questa modifica strutturale e, cosa importante per il Business, le prenotazioni gestite
dal server erano andate correttamente in porto.
Vantaggi ottenuti
L’introduzione della metodologia Scrum ha migliorato l’organizzazione del lavoro
individuale e di team. Gli obiettivi da raggiungere nell’arco di uno sprint sono chiari a tutti,
così come chiari a tutto sono i task a cui il team ha lavorato, stà lavorando o lavorerà nel
giro di qualche giorno. Scrum ha anche eliminato la “burocrazia”, favorendo il dialogo tra
uno o più sviluppatori e gli Stakeholders del prodotto che è in lavorazione. I canali di
contatto e lo scambio di informazioni sono il più diretto possibile.
La tecnica Test Driven Development (TDD) ha portato due grossi vantaggi. Il primo
riguarda la garanzia che il codice scritto rispetti i criteri di accettazione che accompagnano
ogni storia in lavorazione. Se i test, che sono modellati sulla base dei criteri di accettazione,
hanno esito positivo, possiamo ritenere con ragionevole certezza che il codice scritto è
corretto, a meno che non siano stati commessi errori od omissioni nella definizione dei
criteri di accettazione, ma in tal caso il problema sarebbe facile da individuare. Il secondo
riguarda proprio la presenza stessa dei test. Spesso vengono “snobbati” in quanto ritenuti
poco utili, poco stimolanti o, peggio, una perdita di tempo. Il TDD impone, a fronte di un
investimento di tempo relativamente contenuto, la scrittura del codice relativo ai test come
parte integrante del processo di coding.
L’introduzione di test automatici ha confinato i test manuali alle sole funzionalità aggiunte
o modificate. Per le quali è bene che il Product e/o il Business Owner verifichino che quanto
sviluppato effettivamente soddisfi i requisiti.
La sicurezza che quel che funzionava prima dei nuovi sviluppi continui a funzionare, e che
nel sistema non sono stati introdotti bug o regressione, è garantita dai test automatici.
Sempre ai fini della correttezza e della validità del codice scritto, è stata molto importante
l’adozione di tecniche quali Code Review e Pair Programming. Avere codice verificato da
92
7. Conclusioni
almeno un secondo sviluppatore o, meglio ancora, scritto a quattro mani, riduce
ulteriormente il rischio che siano presenti bug indesiderati e garantisce che il codice scritto
è comprensibile e pulito. Un codice pulito sarà sicuramente più facile da manutenere e
modificare in futuro.
L’introduzione della Pipeline ha migliorato i processi di testing manuale e rilascio
dell’applicazione web in diversi ambienti.
Avere un server interno di sviluppo erogante la versione attualmente in lavorazione
dell’applicazione web, permette a chiunque abbia necessità di verificare lo “stato dell’arte”
di farlo semplicemente connettendosi a tale server.
Il rilascio, in qualunque ambiente, è ridotto a poche semplici mosse. Aggiornamento del
server web Tomcat, download da repository centralizzato del pacchetto war,
scompattamento di tale pacchetto in una directory del server web e riavvio del server. In
ottica push button deploy, queste operazioni sono eseguite in automatico da uno script
presente sui vari server. In questo caso, le operazioni da fare si riducono ad una.
Non dover più svolgere una serie di lavori manuali per rilasciare l’applicazione significa
eliminare i rischi correlati al fattore umano. L’esecuzione manuale di attività di basso livello
fortemente ripetitive e poco stimolanti comportano un discreto rischio di errore. Allo stesso
tempo, i rilasci sono più veloci e possono essere eseguiti da chiunque abbia la possibilità
(accesso e permessi) di eseguire uno script su di un server.
Questi fattori agevolano i rilasci frequenti. I rilasci frequenti vanno di pari passo con le
attività svolte in uno sprint (storie piccole che aggiungono funzionalità incrementali) e
riducono la differenza tra la versione in rilascio e quella precedente. Queste differenze lievi
riducono l’impatto che il rilascio ha sull’organizzazione in termini di entità e durata dei
controlli da fare, di stress che il processo inevitabilmente comporta a tutti, riducendo il
rilascio a poco più di una importante operazione di routine.
Da segnalare anche, sempre collegata ai rilasci frequenti ed alle piccole differenze
incrementali che vanno on line con ogni rilascio, il fatto che, nel caso qualcosa vada storto
durante il rilascio o emergano problemi non rilevati in precedenza, un temporaneo ritorno
alla versione precedente (rollback) causa problemi di entità ridotta. 93
7. Conclusioni
Riassumiamo in una tabella comparativa alcuni aspetti legati allo sviluppo ed al rilascio
dell’applicazione Spiderfly prima e dopo l’adozione del Continuous Development. ASPETTO
Prima
Dopo
Durata ciclo sviluppo
~ 1 mese
2 settimane e 1/2
Copertura test automatici
Assente
Presente su ogni funzionalità
Durata rilascio
~1 giorno
~1 ora
Numero versioni patch
Variabile
Max 3
Stop Commit
Flessibile
Categorico
Frequenza rilasci
>50 giorni
Ogni 20 giorni
Delta tra versioni
Alto
Molto basso
Rischio Bug
Alto
Contenuto
Lezione appresa
Volendo sintetizzare in poche righe quanto appreso nel corso di questa attività, si potrebbe
dire che gli sviluppatori, così come i sistemisti, i tester e chiunque sia coinvolto in un
processo di sviluppo software, devono svolgere attività stimolanti e non ripetitive. Con
l’evoluzione tecnologica attuale, i lavori ripetitivi possono, e devono, essere demandati alle
macchine.
Per uno sviluppatore è più stimolante dedicare la maggior parte del proprio tempo e della
propria energia allo sviluppo di nuove funzionalità piuttosto che al testing dell’intero
sistema per verificare che i propri sviluppi non abbiano compromesso altre funzionalità per
qualche oscura ragione. Sviluppatori e sistemisti sono più propensi a collaborare alla
scrittura e manutenzione di script per il rilascio automatizzato piuttosto che dividersi
operazioni manuali da eseguire allo sfinimento durante i processi di rilascio.
Allo stesso modo, il lavoro del tester deve essere quello di definire scenari di test particolari
legati alle storie in sviluppo e convertirli in test automatici in collaborazione eventualmente
94
7. Conclusioni
con uno o più sviluppatori, ricorrendo a framework ad hoc (ad esempio Cucumber) qualora
fosse necessario.
Prospettive future
L’attività conclusa rappresenta un punto di partenza, non certo di arrivo. Le attività che sono
state svolte e le metodologie adottate hanno raggiunto l’obiettivo di ridurre l’intervallo di
tempo tra le richieste del Business ed il rilascio del software che le soddisfa e,
contestualmente, garantire la continuità del servizio fornito dal software esistente.
Analizziamo alcuni aspetti sui quali può valer la pena insistere in futuro per aumentare
ulteriormente la qualità del lavoro di ammodernamento svolto fino ad ora.
Refactoring e copertura test
Durante gli interventi fatti sul codice del software per inserire i test automatici, ci si è spesso
imbattuti in codice obsoleto. Composto da classi e metodi difficili da leggere e manutenere
a causa, soprattutto, delle continue aggiunte subite nel corso degli anni a fronte di poco
refactoring. Anche e soprattutto per questo motivo si è deciso di non procedere con la
predisposizione di test unitari a favore di test leggermente più complessi, che verifichino il
completo funzionamento dell’applicazione.
Questa è, e deve essere, una situazione transitoria, alla quale deve fare seguito un graduale
refactoring del codice sorgente al fine di renderlo più comprensibile e facile da manutenere
ed una eliminazione di classi e metodi non più in uso. Le classi ed i metodi che verranno
predisposti tramite l’attività di refactoring dovranno essere adeguatamente coperti da test
unitari. Quando la copertura di test unitari sarà adeguata, i test End2End attualmente eseguiti
al posto dei test unitari potranno essere ampliati e svolti con frequenza diversa rispetto a
quella richiesta dai test unitari. Sistema modulare
Attraverso l’attività svolta sul modulo Spiderfly, il team di sviluppatori, con il supporto dei
sistemisti, ha preso in mano ed automatizzato il processo di rilascio. Questo dimostra che è
possibile affidare ad uno o pochi team la completa responsabilità di un modulo. Oltre a
curare gli sviluppi, il team ne pianifica e segue i rilasci. Se i rilasci possono essere fatti
garantendo la continuità del servizio, e le modifiche rilasciate non introducono modifiche
bloccanti al contratto con cui i moduli client interagiscono con Spiderfly, non è necessario
nessun tipo di coordinamento con i responsabili dei moduli client in fase di rilascio del
modulo.
95
7. Conclusioni
In un’ottica di evoluzione del sistema Volagratis, verso un’architettura composta da tanti
moduli, ognuno con responsabilità circoscritte, seguito in tutti gli aspetti (sviluppo e
rilascio) da uno o pochi team, Spiderfly potrebbe essere il primo di una serie di moduli di
questo tipo. Alcuni, in verità, sono già stati sviluppati o evoluti da una struttura Legacy ad
una più moderna, altri lo saranno a breve.
96
Indice delle figure
Indice delle figure
Figura 1 Grafico del modello a cascata ................................................................................. 4
Figura 2 Modello modificato con possibilità ritorno a fase precedente ................................ 5
Figura 3 Modello modificato, con possibile ritorno alle fasi di Design ed Analisi dei
requisiti.................................................................................................................................. 6
Figura 4 Modello Sashimi ..................................................................................................... 6
Figura 5 Waterfall with subprojects ...................................................................................... 7
Figura 6 Risk reduction spiral (da [4]) .................................................................................. 7
Figura 7 Modello risk reduction: le aree colorate indicano fasi che tipicamente sono svolte
con approccio a spirale .......................................................................................................... 8
Figura 8 Impatto degli errori fatti in fase di raccolta delle specifiche sulle fasi successive. 9
Figura 9 Evoluzione a spirale del modello a cascata .......................................................... 21
Figura 10 Le “Planning Poker Cards”. Il Planning Game è, appunto, un “gioco”. Ogni
membro del team può usare una carta per esprimere la sua stima. ..................................... 25
Figura 11 Un team Scrum davanti alla propria “board” ..................................................... 30
Figura 12 Esempio di backlog di sprint, sono indicati gli estremi dello sprint e (alcune)
storie in lavorazione. ........................................................................................................... 32
Figura 13 Una Scrum Board alla quale sono appese le storie appartenenti allo sprint
(colonne) e il loro stato (righe). .......................................................................................... 34
Figura 14 Esempio di User Story così come appare sulla Scrum Board............................. 35
Figura 15 Descrizione e criteri di accettazione della storia di cui sopra ............................. 35
Figura 16 Stand up meeting in corso................................................................................... 36
Figura 17 Esempi di burndown chart sull’asse delle ascisse vi sono i giorni sprint, sulle
ordinate gli Story Points residui .......................................................................................... 36
Figura 18 Schema logico che illustra come Continuous Delivery e Continuous
Deployment siano estensioni della Continuous Integration. ............................................... 41
Figura 19 Schema astratto della pipeline. ........................................................................... 41
Figura 20 Esempio di sequence diagram di una deployment pipeline eseguita
automaticamente ad ogni commit su sistema VCS. Notare l’importanza del feedback ad
ogni stato. ............................................................................................................................ 42
Figura 21 DevOps significa collaborazione tra reparti ....................................................... 54
Figura 22 Schema dell’architettura del sistema agli inizi dell’attività del sito (2004) ....... 58
Figura 23 La home page del 2004 (da web.archive.org) e quella attuale (2015) a confronto.
............................................................................................................................................. 59
Figura 24 Schema architettura attuale. Per ragioni di semplicità si è illustrata solamente la
parte relativa a ricerca e prenotazione Voli......................................................................... 60
Figura 25 Sequence Diagram del processo di ricerca fatto usando l’applicazione Volagratis
............................................................................................................................................. 62
Figura 26 Screenshots dell’app mobile Volagratis. Alimentata dall’applicazione Web
Services ............................................................................................................................... 63
Figura 27 Esempio di (semplice) classe (sopra) e della corrispondente classe con test
unitario (sotto) ..................................................................................................................... 71
97
Indice delle figure
Figura 28 Batches o applicazioni per smoke o end2end test che interagiscono con
l’applicazione da testare ..................................................................................................... 72
Figura 29 Esempio estratto da pom.xml di un progetto Maven. Vengono definiti il
packaging del progetto e le dipendenze ............................................................................. 74
Figura 30 Esempio console Jenkins ................................................................................... 75
Figura 31 Esempio di scenario di test per programma che effettua divisioni .................... 76
Figura 32 Mapping in Java dello scenario di cui sopra. I metodi accettano un parametro in
input, quindi possono essere usati anche in scenari differenti............................................ 76
Figura 33 Output prodotto dal run dello scenario di test .................................................... 77
Figura 34 Esempio test con Mok ........................................................................................ 77
Figura 35 Configurazione oggetti Mock ............................................................................ 78
Figura 36 Visione d’insieme del processo (icone Jenkins e Tomcat tratte dai rispettivi siti
web) .................................................................................................................................... 79
Figura 37 Directory contenenti file di configurazione ....................................................... 80
Figura 38 Definizione property nel file di avvio di Tomcat ............................................... 80
Figura 39 Estratto dal file di caricamento dei contesti Spring ........................................... 81
Figura 40 Esempio dichiarazione e iniezione mock per chiamate verso sistemi esterni.... 82
Figura 41 Scenario Cucumber per test flusso creazione pratica prenotazione di una
compagnia aerea ................................................................................................................. 83
Figura 42 Diagramma di flusso della pipeline ................................................................... 85
Figura 43 Console Jenkins progetto Spiderfly ................................................................... 86
Figura 44 Configurazione progetto in downstream ............................................................ 86
Figura 45 Triggering di un processo in downstream .......................................................... 86
Figura 46 Output processo di rilascio e smoke test ............................................................ 87
Figura 47 Applicazione aggiornata up and running ........................................................... 87
98
Bibliografia
Bibliografia
[1]
Wikipedia Community, «Ingegneria del Software,» Wikimedia Fundation,
[Online]. Available: http://it.wikipedia.org/wiki/Ingegneria_del_software.
[Consultato il giorno 7 03 2015].
[2]
W. W. Royce, «Managing the development of large software systems,» [Online].
Available:
http://leadinganswers.typepad.com/leading_answers/files/original_waterfall_paper
_winston_royce.pdf. [Consultato il giorno 26 02 2015].
[3]
T. Bell e T. A. Thayer, Software requirements: Are they really a problem?, IEEE
Computer Society Press, 1976.
[4]
S. McConnell, Rapid Development: Taming Wild Software Schedules, Redmond:
Microsoft, 1996.
[5]
G. Destri, Sistemi Informativi: Il Pilastro Digitale Di Servizi E Organizzazioni,
Milano: Franco Angeli, 2013.
[6]
K. Brennan, A Guide to the Business Analysis Body of Knowledge (BABOK
Guide), Toronto: International Institute of Business Analysis, 2009.
[7]
Agile Programming, «Methodology,» [Online]. Available:
http://agileprogramming.org/. [Consultato il giorno 16 11 2014].
[8]
Agilemanifesto.org, «Manifesto for Agile Software Development,» 2001.
[Online]. Available: http://agilemanifesto.org/. [Consultato il giorno 16 11 2014].
[9]
A. Gutierrez, «Waterfall vs. Agile: Can They Be Friends?,» Agile Zone, 6 2 2010.
[Online]. Available: http://agile.dzone.com/articles/combining-agile-waterfall.
[Consultato il giorno 17 11 2014].
[10] A. Gutierrez, «Waterfall vs. Agile: Development and Business,» Agile Zone,
[Online]. Available: http://agile.dzone.com/articles/waterfall-vs-agiledevelopment-business. [Consultato il giorno 17 11 2014].
[11] D. Wells, «Extreme Programming: A Gentle Introduction,» [Online]. Available:
http://www.extremeprogramming.org/. [Consultato il giorno 17 11 2014].
[12] D. Wells, «Extreme Programming Values,» [Online]. Available:
http://www.extremeprogramming.org/values.html. [Consultato il giorno 19 11
2014].
99
Bibliografia
[13] Wikipedia Community, «Scrum (software development),» Wikimedia Foundation,
[Online]. Available: http://en.wikipedia.org/wiki/Scrum_(software_development).
[Consultato il giorno 23 11 2014].
[14] T. Birch, «Agile Advice,» [Online]. Available:
http://www.agileadvice.com/2014/03/20/referenceinformation/new-scrumdiagram-meet-scrum-by-travis-birch-csp/. [Consultato il giorno 01 03 2015].
[15] J. Humble e D. Farley, Continuous Delivery, Upper Saddle River, NJ: AddisonWesley, 2011.
[16] M. Fowler, «Continuous Integration,» Martinfowler.com, [Online]. Available:
http://www.martinfowler.com/articles/continuousIntegration.html. [Consultato il
giorno 9 12 2014].
[17] Wikipedia Community, «DevOps,» Wikimedia Fundation, [Online]. Available:
http://en.wikipedia.org/wiki/DevOps. [Consultato il giorno 26 12 2014].
[18] K. Beck, Extreme Programming EXplained: Embrace Change, Reading: AddisonWesley, 2000.
[19] Wikipedia, «Scrum (software Development),» Wikimedia Foundation, [Online].
Available: http://en.wikipedia.org/wiki/Scrum_(software_development).
[Consultato il giorno 23 11 2014].
[20] M. Finelli, «Sistemi Di Monitoring, Logging e Alerting Moderni,» [Online].
Available: http://www.slideshare.net/Codemotion/mla-moderni-finelli.
[Consultato il giorno 26 12 2014].
[21] Bravofly, IT Dept., Architettura sistema ricerca voli.
[22] Bravofly, IT Dept., Convenzioni in tema sviluppo software.
[23] Bravofly, Press Office, Descrizione Ufficiale Volagratis.
[24] J. Allspaw e P. Hammond, «10 Deploys Per Day: Dev and Ops Cooperation at
Flickr,» [Online]. Available: http://www.slideshare.net/jallspaw/10-deploys-perday-dev-and-ops-cooperation-at-flickr. [Consultato il giorno 26 12 2014].
[25] Ansible Inc., «Ansible Documentation,» [Online]. Available:
http://docs.ansible.com/. [Consultato il giorno 13 02 2015].
[26] The Apache Software Foundation, «Apache Tomcat,» [Online]. Available:
http://tomcat.apache.org/. [Consultato il giorno 06 03 2015].
[27] Ansible Inc., «Ansible Is Simple IT Automation,» [Online]. Available:
http://ansible.com/. [Consultato il giorno 13 02 2015].
100
Bibliografia
[28] The Apache Software Fundation, «Apache Subversion,» [Online]. Available:
http://subversion.apache.org/. [Consultato il giorno 15 02 2015].
[29] J. Humble, «Continuous Delivery,» Continuous Delivery, [Online]. Available:
http://continuousdelivery.com/. [Consultato il giorno 9 12 2014].
[30] Cucumber Ltd., «Making BDD Fun,» Cucumber, [Online]. Available:
http://cukes.info/. [Consultato il giorno 05 02 2015].
[31] D. Wells, «Extreme Programming Rules,» [Online]. Available:
http://www.extremeprogramming.org/rules.html. [Consultato il giorno 19 11
2014].
[32] Agilemanifesto.org, «I Principi Sottostanti Al Manifesto Agile,» [Online].
Available: http://agilemanifesto.org/iso/it/principles.html. [Consultato il giorno 16
11 2014].
[33] JUnit, «About,» [Online]. Available: http://junit.org/. [Consultato il giorno 15 02
2015].
[34] The Apache Software Fundation, «Maven – Welcome to Apache Maven,»
[Online]. Available: http://maven.apache.org/. [Consultato il giorno 05 02 2015].
[35] Jenkins CI community, «Welcome to Jenkins CI!,» [Online]. Available:
http://jenkins-ci.org/. [Consultato il giorno 05 02 2015].
[36] V. Cardellini, «Lucidi Architetture dei Calcolatori,» 2004. [Online]. Available:
http://www.ce.uniroma2.it/courses/ac05/lucidi/Intro_4pp.pdf. [Consultato il
giorno 20 01 2015].
[37] Wikipedia Community, «Behavior-driven Development,» Wikimedia Fundation,
[Online]. Available: http://en.wikipedia.org/wiki/Behavior-driven_development.
[Consultato il giorno 05 02 2015].
[38] Wikipedia Community, «Modello a Cascata,» Wikimedia Fundation, [Online].
Available: http://it.wikipedia.org/wiki/Modello_a_cascata. [Consultato il giorno
12 2014].
[39] Wikipedia Community, «Modified Waterfall Models,» Wikimedia Fundation,
[Online]. Available: http://en.wikipedia.org/wiki/Modified_waterfall_models.
[Consultato il giorno 11 2014].
[40] Wikipedia Community, «Waterfall Model,» Wikimedia Fundation, [Online].
Available: http://en.wikipedia.org/wiki/Waterfall_model. [Consultato il giorno 11
2014].
101
Bibliografia
[41] ThoughtWorks Inc., «Continuous Delivery Agile Development and Experience
Design,» [Online]. Available: http://www.thoughtworks.com/continuous-delivery.
[Consultato il giorno 9 12 2014].
102
Bibliografia
103