Test Driven Development

Il test driven development (TDD) è una tecnica di progettazione del software che mira a far emergere “dal basso” il design più semplice in grado di risolvere un dato problema. Non si tratta ne un’attività di verifica ne di scrittura del codice, quanto piuttosto un approccio alla scrittura di questi ultimi.

Il TDD si fonda su due concetti fondamentali, esplicitati nella seguente citazione:

TDD = test-first + baby steps

Il significato di questa espressione è che per scrivere del codice che esalti la semplicità della soluzione è necessario scrivere prima il test rispetto al codice (test-first) e procedere a piccoli passi (baby steps), realizzando cioè piccole porzioni di codice, testandole e solo allora andando avanti. Questa tecnica mira infatti a stabilire un ciclo di feedback istantaneo: facendo piccoli passi e testando ogni volta ciò che si appena scritto è meno probabile buttare molto tempo su una soluzione che non funziona, e anche in caso di errore è più facile individuare cosa lo genera e come risolverlo.

Per applicare questo approccio test-driven allo sviluppo effettivo di software, il TDD ha sviluppato il seguente “mantra”: rosso, verde, refactoring. Quando si scrive codice bisogna infatti seguire le seguenti tre fasi:

  • Ogni volta che si deve aggiungere una feature si scrive prima il test che la provi; non essendo ancora stata sviluppata, tale test dovrà fallire (rosso).

  • Si cerca poi di soddisfare il test il più velocemente possibile, facendolo diventare verde. Si ottiene così del codice corretto ma probabilmente molto brutto, quasi come fosse una bozza: tale codice serve però come feedback del fatto che l’algoritmo scelto funziona.

  • Si compie infine un’azione di refactoring (fattorizzazione), ovvero si riorganizza e si riscrive il codice in modo da renderlo migliore assicurandosi però che il test continui ad essere soddisfatto (in questa fase dobbiamo rimanere in uno stato di verde).

Questa ciclo in tre fasi va ripetuto con una cadenza frequente, ogni 2-10 minuti: ciò obbliga a concentrarsi su compiti semplici evitando così di perdersi in costruzioni software complicate che magari non funzionano neanche. Si preferisce invece prima fare qualche piccolo progresso (increment) e poi semplificare per migliorare il codice (simplify).

È importante inoltre capire perché quel passaggio intermedio, la “bozza” menzionata al secondo punto dell’elenco precedente, è tanto importante: concentrarsi in primo luogo sulla creazione di una base funzionante permette subito di capire se si è scelta la strategia giusta per risolvere il problema corrente. Scrivere direttamente il codice “in bella” impiegherebbe molto più tempo e potrebbe non produrrebbe neanche un codice funzionante, siccome maggiore è la complessità del codice che si scrive più è probabile commettere errori.

In virtù di quanto appena detto, l’uso del TDD come tecnica di progettazione garantisce inoltre due importanti vantaggi:

  • Spesso capita di scrivere codice difficilmente testabile: scrivere il test prima e il codice dopo aiuta invece a progettare prodotti la cui correttezza può essere provata.

  • Scrivere prima i test aiuta a definire chiaramente le interfacce del programma e come queste comunicano tra di loro, mentre se non dovessimo farlo potremmo avere delle dipendenze complicate da rimuovere.

Durante il testing ci si pone dal punto di vista del cliente: la tecnica TDD ci permette dunque di osservare il codice da molteplici prospettive (sviluppatore e cliente), cosa che contribuisce ovviamente alla creazione di un prodotto migliore.

eXtreme Programming (XP)

Ora possiamo iniziare a parlare di Extreme Programming (XP), una tecnica di sviluppo agile nata tra la fine degli anni ‘90 e l’inizio degli anni 2000 dalla mente di Kent Beck, che la ideò nell’ambito di un progetto Chrysler.

Le variabili in gioco

Secondo Beck, durante lo sviluppo di software le principali variabili sono:

  • portata: la quantità di funzionalità da implementare, una variabile delicata dal valore mutevole poiché il numero di funzionalità richieste può cambiare nel corso dello sviluppo;
  • tempo: il tempo che si può dedicare al progetto;
  • qualità: la qualità del progetto che si vuole ottenere, principalmente relativa a correttezza e affidabilità;
  • costo: le risorse finanziare che si possono impegnare per il progetto.

Queste 4 variabili non sono indipendenti tra di loro, in quanto cambiare una influenza automaticamente le altre, in positivo o in negativo. Ponendo quindi che la qualità non sia negoziabile (il software deve funzionare) bisognerà lavorare sulle altre, specialmente bilanciando costo e tempo.

Nel panorama classico di sviluppo la portata era definita in modo rigido dal cliente, che richiedeva certe funzionalità non negoziabili e pagava lo sviluppatore a progetto completo. Con l’XP si stravolge invece la prospettiva: il costo è orario, il tempo disponibile non è fisso ma pari al tempo richiesto per lo sviluppo e la portata viene ricalcolata durante il progetto, essendo così l’unica variabile a variare effettivamente. Si tratta di un approccio incrementale che mira ad avere sempre un prodotto consegnabile se il cliente decide di essere soddisfatto dello sviluppo: non si fa aspettare il cliente per dargli tutto il lavoro in un colpo solo, ma questo viene consegnato una parte alla volta. Oltre ad alleggerire la pressione sullo sviluppatore, questo approccio è utile per due motivi:

  • Il cliente è certo che lo sviluppatore si sia dedicando al progetto siccome vede il prodotto crescere a poco a poco.
  • Dà la possibilità al cliente di avere comunque qualcosa in mano se ad un certo punto vuole interrompere la collaborazione.
  • Permette al cliente di cambiare idea sulla portata e sulle funzionalità richieste in corso d’opera, bandendo la rigidità dei documenti di specifica.

Tutti questi aspetti permettono di creare un rapporto molto meno conflittuale tra cliente e sviluppatore, cosa che crea le basi per una maggiore collaborazione tra le due parti.

Principi dell’extreme programming

Parliamo ora un po’ dei fondamenti della filosofia XP, confrontandoli con quanto veniva prescritto nell’ambiente di sviluppo classico. I principi dell’ingegneria del software classica erano infatti i seguenti:

  • Separazione degli interessi (aspects o concerns): separare tempi, responsabilità e moduli, ovvero tutte le varie viste o le varie dimensioni su cui si deve affrontare il problema.
  • Astrazione e modularità: bisogna usare le giuste astrazioni che ci permettono di dominare i problemi complessi (possono essere i diversi linguaggi di programmazione, linguaggi di descrizione o vari altri costrutti).
  • Anticipazione del cambiamento (design for change): in fase di progettazione il programmatore deve pensare a come potrebbe cambiare il prodotto, accomodando la possibile aggiunta di requisiti che il cliente magari non aveva neanche pensato; bisogna stare attenti però, perché spesso questo concetto complica arbitrariamente la progettazione e lo sviluppo, rischiando di far perdere molto tempo su cose che al cliente potrebbero non servire: può essere un’idea migliore partire da qualcosa di semplice ed incrementare man mano.
  • Generalità: per rendere più semplice la modifica e l’espansione futura è necessario scrivere interfacce molto generali ai sistemi che costruiamo.
  • Incrementalità: lo sviluppo avviene incrementalmente, un pezzetto alla volta.
  • Rigore e formalità: è importante essere rigidi e specifici sia nella comunicazione che nella descrizione dei requisiti.

Sebbene non butti via tutti questi principi ma ne erediti invece alcuni per adattarli alle proprie esigenze (specialmente la separazione degli interessi, che viene data per scontata), l’XP pone l’accento su altri aspetti, ovvero:

  • Feedback rapido: bisogna mantenere un costante flusso di feedback; questo viene dato dai test, dai colleghi ma anche dal cliente, che dev’essere continuamente consultato sullo stato dei lavori. Tra le iniziative che favoriscono un veloce ciclo di feedback c’è lo standup meeting, una riunione mattutina fatta in piedi in cui ciascuno descrive in poche parole cosa ha fatto il giorno precedente e cosa intende fare oggi.
  • Presumere la semplicità: non bisogna complicare senza motivo né il codice, che dev’essere scritto con in mente ciò che serve a breve termine e non in un futuro remoto, né le relazioni tra colleghi, che non devono essere eccessivamente gerarchiche (tutti dovrebbero avere compiti molto simili); in generale si dovrebbe semplificare il più possibile in tutti gli ambiti del progetto.
  • Accettare il cambiamento: non ci si deve aspettare che il software sia immutabile; al contrario, deve essere dato per scontato il concetto di flessibilità e malleabilità, ovvero che il cliente vorrà fare cambiamenti sia dopo che durante lo sviluppo del prodotto.
  • Modifica incrementale: ritornando al concetto di baby steps, ogni iterazione di sviluppo dovrebbe essere breve e le funzionalità introdotte piuttosto piccole; questa regola si applica tuttavia a tutti gli ambiti del progetto, tra cui la gestione del team: ovvero non bisognerebbe mai aggiungere più di una persona alla volta al gruppo di lavoro, in quanto aggiungerne di più potrebbe portare a passare più tempo ad istruirle che a sviluppare.
  • Lavoro di qualità: bisogna ovviamente ottenere un buon prodotto, ma per fare ciò la prospettiva cambia in favore dello sviluppatore, al quale si deve garantire un ambiente di lavoro salutare e un certo benessere; la fidelizzazione dei programmatori è importante perché più si trovano bene e meglio lavorano.

I due punti più in contrasto sono il presumere la semplicità e l’anticipazione del cambiamento: ci sembra infatti più previdente pianificare per il futuro e anticipare eventuali cambiamenti, ma come vedremo nel prossimo paragrafo talvolta questo può essere controproducente.

Presumere la semplicità vs anticipazione del cambiamento

XP mette davanti la semplicità all’anticipazione del cambiamento: non si scrive in anticipo codice che si pensa servirà in futuro. Questo non significa che non si stia progettando per il futuro, ma solo che questo non è il primo aspetto da guardare: il primo aspetto è la semplicità, ovvero fare le cose nella maniera più chiara possibile.

Non pianificare per il futuro sembra rischioso: secondo uno studio condotto da Bohem nel 1976 viene ipotizzata una curva esponenziale per il corso delle modifiche all’aumento dell’avanzamento del progetto; più il progetto avanza più è costoso modificarlo, motivo per cui sembra necessario accomodare il cambiamento futuro in modo da ridurre tale costo.
Al contrario, XP presuppone una curva di tipo logaritmico che tenda ad un asintoto: passato un certo punto nello sviluppo il costo per le modifiche non subisce più cambiamenti sensibili, per cui non ha senso fasciarsi la testa in anticipo in quanto un codice semplice è relativamente facile da modificare.

Va inoltre considerato che Bohem parlava in realtà di cost-to-fix, non del costo per la modifica in sé; inoltre la sua statistica era poco affidabile poiché era stata costruita a partire da pochi dati. La curva esponenziale da lui descritta è stata poi successivamente ritrattata per accomodare il fatto che se un errore avviene in una fase affligge solo le successive, e non le precedenti.

Figure in gioco e responsabilità

Al fine di organizzare il lavoro, XP individua diverse figure che partecipano allo sviluppo:

  • Cliente: colui che richiede funzionalità e conosce il dominio applicativo.
  • Sviluppatore: colui che sviluppa concretamente scrivendo codice.
  • Manager: colui che amministra lo sviluppo con uno sguardo generale.

È interessante l’inclusione del cliente nel contesto dello sviluppo: esso non è più soltanto il committente ma ha un ruolo attivo nel lavoro, potendo cioè contribuire alla riuscita del progetto anche e soprattutto in virtù della già citata conoscenza del dominio applicativo.

Ciascuna di tali figure ha responsabilità e diritti riassunti nella seguente tabella (manager e cliente sono accorpati perché hanno grossomodo gli stessi compiti):

Soggetto Ha responsabilità di decidere... Ha diritto di...
Manager/Cliente
  • Portata del progetto, ovvero le funzionalità da realizzare
  • Priorità tra funzionalità e loro business value
  • Date dei rilasci, anche nel caso di release incrementali
  • Sapere cosa può essere fatto, con quali tempi e quali costi
  • Vedere progressi nel sistema, provati dai test da lui definiti (trasparenza)
  • Cambiare idea, sostituendo funzionalità o cambiandone le priorità a intervalli di tempo fissi (fine del ciclo di sviluppo incrementale)
Sviluppatore
  • Stime dei tempi per le singole funzionalità (no deadline imposte dall'alto)
  • Scelte tecnologiche e loro conseguenze, ovvero come si realizzano le funzionalità richieste
  • Pianificazione dettagliata delle iterazioni
  • Ricevere dei requisiti chiari (casi d'uso) con priorità per le varie funzionalità
  • Cambiare le stime dei tempi man mano che il progetto procede e il contesto di sviluppo cambia
  • Identificare funzionalità pericolose o troppo difficili da realizzare
  • Produrre software di qualità, per il quale deve godere di un certo benessere

Come si vede, per migliorare la fiducia tra sviluppatore e cliente sono necessari due requisiti: un certo grado di trasparenza da parte di chi sviluppa, ottenuta dall’uso delle contiene release incrementali per mostrare come sta evolvendo il sistema, e una certa dose di pazienza da parte del cliente, che deve accettare di lasciare allo sviluppatore la facoltà di decidere come si realizzano le funzionalità e di cambiare le prospettive temporali di sviluppo qualora fosse necessario.

Tecniche XP

L’extreme programming fornisce una serie di metodologie pratiche per poter garantire tutto ciò che è stato descritto fino ad ora. Lo schema sottostante le descrive mettendole in relazione tra loro in modo che i vari aspetti negativi delle diverse pratiche siano compensati dagli aspetti positivi di quelle in relazione con loro; in sostanza abbiamo un mix perfetto di attività organizzate in modo da garantire i buoni principi di cui sopra.

1. Planning game

È l’attività di pianificazione che viene fatta all’inizio di ogni iterazione e serve per “congelare” il sottoinsieme di requisiti sul quale il team lavorerà per le prossime ~2 settimane.

Si parte dalle richieste del cliente espresse tramite user stories, una versione semplificata degli use case degli UML; esse hanno come soggetto sempre un ruolo specifico nell’azienda del cliente e descrivono una funzionalità. Ogni user story è dunque composta da tre parti:

  • il soggetto, ovvero il ruolo dell’utente nell’azienda (può anche essere esterno);
  • l’azione che vuole eseguire il soggetto;
  • la motivazione che spinge il soggetto a portare avanti l’azione descritta.

Esempi di user stories potrebbero essere:

  • Da bibliotecario, voglio poter visualizzare dove si trova un particolare libro in modo da poterlo reperire per i clienti.

  • Da utente della biblioteca, voglio poter visualizzare lo stato di un libro per poterlo prendere in prestito.

Lo scopo del planning game è dunque quello di determinare quali funzionalità saranno presenti nel prossimo rilascio combinando priorità commerciali e valutazioni tecniche: questo richiede una collaborazione da parte del cliente, che come vedremo sarà presente in loco al momento della decisione.

Procedura

Quest’attività di pianificazione si divide fondamentalmente in tre fasi:

  1. All’inizio il cliente compila le carte, nient’altro che pezzetti di carta volutamente piccoli per impedire di scriverci troppo. Su ogni carta è presente:
    • un identificativo numerico;
    • una breve frase che descrive uno scenario d’uso;
    • un caso di test che funge da test d’accettazione della funzionalità: si tratta in sostanza di un paio di esempi, di solito uno positivo e uno negativo, che devono essere soddisfatti per ritenere completa la feature;
    • il valore di business che la funzionalità ha per il cliente.
  2. Per ogni carta il team di sviluppatori fa dunque una stima del tempo necessario a realizzarla: raggiunta una stima comune questa viene scritta sulla carta e servirà per confrontare tale previsione con il tempo effettivamente impiegato, di cui si tiene conto sul suo retro. Per ciascuna carta uno sviluppatore assume infatti il ruolo di tracker, impegnandosi cioè a tracciare lo stato di avanzamento della relativa funzionalità durante le due settimane (es. quante feature fatte, quanti bug segnalati, etc.).

  3. Il manager decide quindi sulla base di queste informazioni quali carte verranno implementate durante prossima iterazione: per questa operazione prende in considerazione il valore delle feature, le dipendenze tra l’una e l’altra e una serie di altri fattori. Se, come dovrebbe essere, le varie funzionalità rappresentate nelle carte sono indipendenti, il manager può compiere questa scelta calcolando il rapporto tra il valore e il tempo stimato e usarlo per ordinare le carte: tuttavia l’operazione richiede una certa dose di ragionamento e non è mai così meccanica.

Le stime

Abbiamo detto che le stime dei tempi vengono fatte dall’intero team in accordo; tuttavia il team è composto da persone diverse che quindi faranno stime diverse in funzione dell’esperienza e delle proprie capacità. È tuttavia importante raggiungere una stima accettata da tutti in quanto il team si impegna a rispettarla: se viene deciso che il tempo per una data scheda è di qualche ora e questa viene assegnata a uno sviluppatore che aveva fatto una stima di qualche giorno allora quest’ultimo si troverà in difficoltà nel portare a termine il compito; per questo motivo è importante il contributo anche degli sviluppatori junior o inesperti.

Al di là del problema del raggiungimento di una stima comune, per il quale vedremo delle tecniche specifiche, ci possono essere una serie di problemi di stima legati alla funzionalità in sé. Potremmo infatti avere stime:

  • molto differenti (ore vs giorni): in questo caso, è possibile che la carta non sia descritta o compresa correttamente; se uno sviluppatore stima poche ore e un altro qualche giorno c’è qualche problema. in conclusione è necessario trovare un punto di incontro.

  • quasi uniformi, ma molto alte: se la stima supera il tempo di iterazione potrebbe essere che la storia sia troppo ampia. Non si può neanche iniziarla in questo ciclo e continuarla nel prossimo: se alla fine dell’iterazione non ho portato a termine il lavoro prefissato è come se non l’avessi fatto (anche se magari era stato completato all’80%), perché il cliente non lo vede nella release e tale lavoro non è dunque dimostrabile. Per ovviare a questo problema si può fare lo splitting delle carte, ovvero scomporre una carta in più carte in modo da dividere il problema in sotto-problemi.

  • non uguali ma simili: non bisogna prendere la più bassa, alta o la media. Come abbiamo già detto, secondo XP bisogna arrivare ad un accordo in modo tale che chiunque nel team si riconosca nella stima effettuata.

Oltre a ciò, la fase di stima dei tempi si porta dietro diverse problematiche intrinseche, tra cui:

  • perdita di tempo: per accordarsi su una stima comune si spende molto tempo (troppa comunicazione);

  • effetto àncora (anchoring effect): si tratta di un effetto che si verifica quando bisogna assegnare un valore ad una quantità ignota. Poiché il cervello umano è più bravo a ragionare per relazioni piuttosto che per assoluti, una volta che viene fatta la prima stima numerica questa definisce l’ordine di grandezza delle stime successive, facendo cioè da punto di riferimento da cui è molto difficile distanziarsi: nel nostro caso quando il team si riunisce per fare delle stime e il primo membro dà la sua opinione, tutte le stime successive orbiteranno intorno ad essa. Tale effetto impedisce di fare una stima che prenda obiettivamente in considerazione le sensazioni di tutti i membri del team, e va dunque assolutamente evitato.

Per evitare questi problemi e semplificare il processo di stima si sono sviluppati diversi processi, che data la loro natura giocosa aumentano anche l’engagement degli sviluppatori in questa fase di pianificazione.

Planning poker

Una per una vengono presentate brevemente le carte con le user stories facendo attenzione a non fare alcun riferimento alle tempistiche in modo da non creare subito un effetto àncora: in questa fase il team può fare domande, chiedere chiarimenti e discutere per chiarire assunzioni e rischi sulla user story, ma deve stare molto attento a non fare alcuna stima.

Dopodiché ogni componente del team sceglie una carta dal proprio mazzo personale per rappresentare la propria stima e la pone coperta sul tavolo: su queste carte si trovano una serie di numeri senza unità di misura che vanno da 0 a 100 seguendo un andamento non uniforme; il loro scopo è quello di definire un’ordine di grandezza piuttosto che una stima precisa. Ci sono anche delle carte particolari, ovvero:

  • il punto di domanda indica che non si è in grado di dare una stima
  • la tazza di caffè indica che la riunione è andata troppo per le lunghe ed è necessaria una pausa.

Fatta questa prima stima blind le carte vengono girate contemporaneamente: idealmente vi dovrebbe essere l’unanimità sulla stima. Se così non è chi ha espresso le stime più basse e più alte ha ~1 minuto per motivare la propria scelta in modo da cercare di convincere gli altri; si noti che agli altri componenti del team non è concesso parlare per evitare di perdere troppo tempo!
Finito questo momento di consultazione tutti i membri del team fanno una nuova stima e si continua così finché non si raggiunge l’unanimità; solitamente le votazioni convergono dopo un paio di round.

Ma qual’è l’unità di misura su cui si fanno le stime? Dipende: essa può essere scelta prima o dopo aver trovato un accordo; possono essere ore, giorni o pomodori (un pomodoro è formato da 25 minuti senza alcuna distrazioni,e dopo c’è una pausa). Ovviamente non si può pretendere di lavorare delle ore senza alcuna distrazione, per cui in queste stime si considera anche un certo slack time, ovvero un tempo cuscinetto per che comprende il “tempo perso” a causa di distrazioni.

Team Estimation Game

Si tratta di un metodo un po più complesso articolato in 3 fasi e basato sul confronto tra i diversi task piuttosto che sulla stima numerica: esso si basa infatti sull’idea che sia semplice stabilire se un task sia più facile o più difficile di un altro, mentre è molto più complicato capire di quanto sia più facile/difficile. L’idea è dunque quella di splittare in fasi questa cosa di dover dare un valore al task considerandone sempre di più difficili per arrivare a fare una buona stima.

PRIMA FASE

Si fa una pila con le storie e si mette la prima carta al centro del tavolo. I developer si mettono in fila e uno alla volta eseguono queste azioni:

  • il primo della fila estrae una carta della pila, la legge ad alta voce e la posiziona a sinistra (più semplice), a destra (più complicata) o sotto (equivalente) la carta già presente sul tavolo.
  • il prossimo developer può:
    • estrarre una nuova carta dalla pila e posizionarla secondo le stesse regole, eventualmente inserendola in mezzo a due colonne già presenti;
    • spostare una carta precedentemente posizionata commentando la motivazione della sua scelta; può ovviamente succedere che tale carta venga rispostata nella sua posizione originale, ma dopo un po’ si troverà un accordo sulla difficoltà del relativo task.

Terminata la pila avremo le carte disposte sul tavolo in colonne di difficoltà comparabile, ordinate dalla meno difficile (sinistra) alla più difficile (destra). Oltre ad aver ridotto la comunicazione (molte carte non saranno contestate), usando questa tecnica abbiamo evitato anche l’effetto àncora rendendolo relativo: l’assenza di valori precisi evita il rischio di influenzare eccessivamente gli altri. Inoltre a differenza del planning poker si può tornare sulle proprie decisioni, cosa che favorisce un continuo adattamento e ripensamento delle stime.

SECONDA FASE

Si cerca dunque di quantificare le distanze tra le carte.

Ci si mette di nuovo in coda davanti al tavolo con il mazzo di carte del planning poker (uno solo, non uno per persona) e si cerca di etichettare le colonne in base alle difficoltà.

Si posiziona la prima carta (solitamente si parte da 2 perchè magari nella prossima iterazione può esserci qualcosa di ancora più facile) sopra la prima colonna.

Quindi:

  • il primo sviluppatore prende il valore successivo e lo posiziona sulla prima colonna che pensa abbia quel valore (rispetto al 2), oppure lo posiziona tra due colonne se pensa che sia un valore di difficoltà intermedio tra le due.
  • lo sviluppatore successivo può invece:
    • estrarre una carta dal mazzo e posizionarla secondo le regole di prima (la prima colonna che pensa abbia un particolare valore di difficoltà);
    • spostare una carta con un valore precedentemente posizionato, commentando la motivazione dello spostamento;
    • passare il turno, nel caso in cui non ci siano più carte nella pila e non si vogliono spostare altre carte.

È possibile avere delle carte in cui sopra non c’è nessun numero, queste saranno assimilate alla colonna alla loro sinistra.

Al termine di questa fase, la situazione sarà simile alla seguente:

TERZA FASE

Si stima il tempo in ore/uomo di una delle carte più semplici e successivamente si calcolano tutte le colonne in proporzione alla prima. Ma questa fase è davvero cosi utile? Nella pratica si è visto che è inutile valutare il lavoro fatto in ore/uomo, anche perchè con il passare del tempo la taratura può variare.

Nella prossima sezione parliamo di come la nozione di velocity risolve questo problema.

Velocity

È importante riuscire a stimare la velocità con la quale stiamo avanzando. In fisica la velocità è data dal rapporto tra la distanza percorsa e il tempo per percorrerla. Questa proprietà può essere usata anche nella gestione dello sviluppo agile: il numeratore è il punteggio delle storie mentre il denominatore è la lunghezza dell’iterazione (assimilabile in un’unità di tempo).

La velocity nel mondo agile è quindi il numero di story point guadagnati nell’arco dell’iterazione corrente.

Essa riesce quindi a dare un’idea di quanto si è riusciti a fare in termini di complessità astratta. Se per esempio il team è riuscito a fare 50 punti nella iterazione appena finita, è ragionevole prefissarsi di fare almento 50 punti nell’iterazione successiva.

La velocity non può essere usata per dare premi, per confrontare team diversi o punire in caso di diminuzione, però si adatta al modo diverso degli sviluppatori di gestire le stime e dal fatto che si tende a sottostimare o sovrastimare carte diverse.

All’atto di aggiungere una persona questa metrica deve inizialmente rimanere invariata, per prevedere la sua formazione; se la rimuovo ci sarà una perdita di produttività.

La velocity non deve considerare le storie lasciate incompiute, quindi anche se l’ho completata al 90% devo considerarla come se non l’avessi fatta. Inoltre, non deve essere imposta: la velocity di un team è fissa e non può essere aumentata.

Esiste un movimento chiamato no estimates, che evita al team tutta la parte delle stime. Dall’esperienza del prof. Bellettini, però, questa metodologia funziona in team molto maturi che sono in grado di guidare il ciente a formulare storie simili in termini di difficoltà, avendo tutti una misura standard per le storie.

2. Brevi cicli di rilascio

Per ridurre i rischi, la vita e lo sviluppo dell’applicazione sono scanditi dai rilasci di diversioni del prodotto funzionanti, di solito uno ogni due settimane (come abbiamo visto in scrum con il freez, ma con un tempo di rilascio minore). È necessario avere abbastanza tempo per sviluppare qualcosa di concreto, e il cliente per poter pensare alle richieste che ha fatto e stabilire se ha bisogno di modifiche.

Betrand Meyer, nel suo libro “Agile! The Good, the Hype and the Ugly”, definisce questa idea “brillante”, “forse l’idea agile con l’influenza e impatto maggiore nell’industria”.

3. Uso di una metafora

Definire un nuovo vocabolario con cui parlare con l’utente (tecnica non informatica) ma anche ai nuovi sviluppatori. Serve per permettere una nominazione di classi e metodi omogenei e fornire una vista d’insieme. Siccome non c’è una vera documentazione in XP, possiamo usare queste metafore come una vista d’insieme, quindi sostituire in parte l’architettura del sistema, e far capire gli elementi fondamentali, il loro scopo e le relazioni fra loro.

4. Semplicità di progetto

Ovvero l’arte di massimizzare il lavoro non fatto, o da non fare. Non è necessario riscrivere cose già esistenti e consolidate.

Uno slogan tipico è KISS: Keep It Simple, Stupid.

Questo punto si contrappone al design for change che viene invece visto come un appesantimento inutile, perchè una feature che aggiungiamo può essere scartata dal cliente.

5. Testing

È consolidato su due fronti:

  • i clienti scrivono i test di accettazione (o funzionali) sulle schede per aumentare la loro fiducia nel programmi;
  • i programmatori scrivono i test di unità perché la fiducia nel codice diventi parte del programma stesso.

Nell’XP ogni aspetto viene massimizzato, ma in particolare il testing viene esasperato di più in quanto, oltre ad essere molto importante, molti altri aspetti si basano su di esso (vedi la figura all’inizio della sezione). Ha il ruolo di rete di protezione in tutte le fasi: ogni cambiamento è verificabile tramite i test.

Il testing aiuta molto anche quando non si parte da zero con il programma, ma quando si deve modificare un programma proprietario precedentemente sviluppato anche in modalità non agile. Prima di apportare modifiche al codice scrivo i test e solo successivamente procedo, in modo da non causare problemi.

Un altro concetto importante è che i test dovrebbero coprire tutte le righe di codice.

6. Refactoring

Anche da novizi, non bisogna avere paura di apportare modifiche che semplificano il progetto: bisogna avere coraggio.

Il refactoring è l’operazione che modifica solo le proprietà interne del software, non le funzionalità. L’obiettivo è eliminare l’entropia generata dalle continue modifiche e aggiunte.

Il refactoring deve essere graduale e continuo in modo da poter aggiungere funzionalità in maniera semplice. Chiaramente, in caso di ristrutturazioni architetturali di grosse dimensioni di sistemi legacy non è sempre possibile procedere in questa maniera.

Parti di codice non stimolate da test non sono utili ai fini della soluzione: o si aggiungono test per gestire i casi specifici, altrimenti si possono rimuovere in toto.

Il refactoring è una delle tecniche più importanti e fondamentali dell’XP.

7. Programmazione a coppie

La programmazione a coppie (pair programming) è una tecnica controintuitiva: dal punto di vista del manager si pagano due persone per fare il lavoro di una, ma non è così.

Ci sono diversi vantaggi:

  • in coppia, ci si controlla a vicenda su ogni aspetto (codice, rispetto delle regole XP, idee);
  • mentre il pilota attua le idee, il navigatore pensa cosa fare subito dopo: forma di refactoring;
  • favorisce l’inserimento di nuovo personale: piuttosto che lasciare i novizi da soli a studiare libroni, vengono affiancati e incitati a osservare e interagire con persone esperte che stanno lavorando;
  • fa ottenere una proprietà collettiva (conoscenza osmotica), come descritta da Crystal. Un altro punto importante sono i commenti naive (ovvero fatti da programmatori junior) per permettere di chiarire concetti basilari date spesso per scontato.

Raddoppiare il numero di persone raddoppia la produttività? No, è stimato invece che la produttività aumenti circa del 50% - quindi non abbastanza per giustificare il costo.

Diversi studi si chiedono se la produttività calcolata puntualmente sia una metrica sensata. Secondo molti no, perché al termine di un’iterazione ciò che sembra poco produttivo in realtà lo è di più: il tempo non successivamente speso in verifica, convalida e refactoring è largamente assorbito dall’ispezione continua del codice svoltasi durante le sessioni di pair programming.

Critiche

Betrand Meyer, nel suo libro “Agile! The Good, the Hype and the Ugly”, scrive:

Applied judiciously, pair programming can unquestionably be useful. Many developers enjoy the opportunity to program jointly with a peer, particularly to deal with a thorny part of an assignment. The basic techniques, in particular the idea of speaking your thoughts aloud for immediate feedback, are well understood and widely applied. (As a manager I regularly hear, from a developer, “On this problem I would like to engage in a round of pair programming with X ”, and invariably find it a good idea.)

What is puzzling is the insistence of XP advocates that this technique is the only way to develop software and has to be applied at all times. Such insistence makes no sense, for two reasons.

The first is the inconclusiveness of empirical evidence, noted above. Granted, lack of data is often used as a pretext to block the introduction of new techniques. When an idea is obviously productive, we should not wait for massive, incontrovertible proof. But here there is actually a fair amount of empirical evidence, and it does not show a significant advantage for pair programming. Pair programming may be good in some circumstances, but if it were always the solution the studies would show it. In the absence of scientific evidence, a universal move is based on ideology, not reason.

The second reason, which may also explain why studies’ results vary, is that people are different. Many excellent programmers love interacting with someone else when they write programs; and many excellent programmers do not. Those of the second kind want to think in depth, undisturbed. The general agile view is that communication should be encouraged and that the days of the solitary, silent genius are gone. Fine; but if your team has an outstanding programmer who during the critical steps needs peace, quiet and solitude, do you kick him out of the team, or force him to work in a way that for him may be torture?

It is one thing to require that people explain their work to others; it is another, quite dangerous, to force a single work pattern, especially in a highly creative and challenging intellectual endeavor. When Linus Torvalds was writing Linux, he was pretty much by himself; that did not prevent him from showing his code, and, later on, engaging thousands of people to collaborate on it.

8. Proprietà collettiva

Il codice non appartiene a una singola persona ma al team: non devono quindi esistere policy di “code owners” a la Microsoft. Tutti i componenti del team sono quindi autorizzati a modificare e sistemare ogni parte del codice, anche se scritta da un altro.

Durante il giorno, più volte al giorno, è comune cambiare coppia e saranno quindi possibili situazioni in cui nessuno dei due ha una profonda conoscenza della parte di codice che si sta trattando o che il task non si addica alle competenze della coppia.

In tutti i casi, in XP ci si riferisce al team e non al singolo.

9. Integrazione continua

Nell’ottica di ricevere feedback rapidi dal cliente è necessario integrare spesso, anche più volte al giorno. Questo non significa far passare i test d’unità per integrare tutto in un’unica operazione, ma essere graduali: è frequente scoprire che parti testate e funzionanti singolarmente una volta integrate nel prodotto finale non funzionano.

L’integrazione continua e graduale è una tecnica largamente utilizzata in tutti i campi, non solo nello sviluppo software.

Al termine dello sviluppo di una feature, è compito della coppia integrarla nella macchina di riferimento. L’accesso a tale macchina deve essere regolato in maniera esclusiva: in situazioni di lavoro da remoto si può utilizzare un token. La macchina di riferimento si trova, per quanto riguarda le funzionalità, in una situazione monotona crescente. Ad ogni integrazione è necessario produrre sempre qualcosa di consegnabile al cliente.

Una user story si definisce completata solo dopo aver terminato l’integrazione, superato dei test di integrazione e aver mostrato al cliente il risultato della macchina complessiva dopo l’integrazione.

Un’altro punto a favore della continua integrazione è che evita la situazione in cui una coppia modifichi la macchina dopo molto tempo dalla propria ultima integrazione, aumentando di molto la probabilità di errori per le altre coppie.

Se una coppia non riesce ad integrare blocca anche tutte le altre che non possono andare avanti con le use story, quindi sarà necessario che quella coppia rinunci, ritorni sulla sua macchina e cerchi di risolvere lì - tutte le coppie hanno una propria macchina su cui testano prima di farlo su quella comune.

10. Settimana di 40 ore

Il mestiere di sviluppatore ha sempre avuto dei ritmi dettati dalle consegne: lavorare troppo a lungo porta a un abbassamento della produttività, oltre che a stress e frustrazione.

Nell’XP si cerca di evitare queste situazioni in modo da avere una resa migliore, avere maggior soddisfazione nel lavorare nel team e nell’azienda, avere meno problemi fuori dal lavoro (tante volte questo eccessivo lavoro può causare problemi familiari) e inoltre abbassare la probabilità per l’azienda di perdere dipendenti.

Purtroppo però il mestiere dello sviluppatore non è meccanico e molto spesso si vuole portare a termine quello che si sta facendo perchè magari si è quasi alla soluzione, inoltre si continua a pensare a come risolvere dei problemi anche fuori dall’orario lavorativo.

11. Cliente sul posto

Dal punto di vista del cliente, il suo inserimento nel luogo fisico di sviluppo è un vantaggio in quanto può essere sicuro che gli sviluppatori stiano lavorando per lui e può verificare come procede il progetto.

Dal punto di vista degli sviluppatori, invece, è fondamentale avere il cliente sul posto per potergli chiedere chiarimenti in caso di specifiche non chiare. La possibilità di poter far domande è come avere una documentazione vivente; il cliente potrà continuare a lavorare per la sua azienda, ma dovrà dare priorità alle richieste degli sviluppatori.

Avere il cliente sul posto ha comunque dei limiti: quest’ultimo, infatti, deve essere scelto accuratamente per avere una persona rappresentativa di tutti gli stakeholder; il compito è forse impossibile. Se il cliente del posto non è disponibile, il team deve trovare dei modi per poter comunque avere un punto di riferimento: la tecnica Scrum introduce il concetto di product owner, un componente interno al team che si comporta come se fosse il cliente.

Il cliente durante le iterazioni può creare altre storie che a partire dall’iterazione successiva potrà inserire nel planning game; è inoltre disponibile per test funzionali.

12. Standard di codifica

È necessario prevedere delle regole (convenzioni comuni) che specificano come scrivere il codice, per aumentare la leggibilità e quindi la comunicazione attraverso il codice.

Spesso, si utilizzano degli strumenti per garantire il rispetto delle convenzioni o autocorreggere il codice auutomaticamente.

Avere uno standard di codifica aiuta inoltre:

  • il refactoring;
  • la programmazione a coppie;
  • la proprietà collettiva.

13. They’re just rules

L’ultima regola “non è canonica”, in quanto è stata aggiunta successivamente da alcuni agilisti.

Al termine di un’iterazione si fa un resoconto e quindi decidere come comportarsi per l’iterazione successiva. Nel suddetto resoconto si può anche decidere di sospendere regole se si pensa che non siano adatte per la situazione o per il team e successivamente possono essere reintrodotte. La decisione di non seguire una regola deve essere sempre fatta a livello di team, non dal singolo o dalla coppia.

In conclusione, l’XP non è una tecnica così rigida e rigorosa: ad ogni iterazione, si possono effettuare test per trovare il giusto equilibrio.

Questo punto non però è condiviso da tutti e una motivazione la si può trovare nel fatto che tutti i punti sono interconnessi tra loro, e quindio non è possibile studiarli singolarmente senza considerate anche gli altri perchè non avrebbero senso in quanto hanno una forte dipendenza l’una dall’altra; non a caso nei punti sopra si può notare come si influenzino a vicenda.

XP e modello a cascata

È possibile tentare di raggruppare le diverse tecniche dell’eXtreme Programming nelle macrofasi descritte dal modello a cascata.

  • Requirements:
    • i clienti fanno parte del team di sviluppo: requirements viventi;
    • consegne incrementali e pianificazioni continue: evoluzione del progetto.
  • Design:
    • metafora come visione unificante del progetto;
    • refactoring: è design puro, molto utile per rendere possibile l’evolvibilità del software;
    • presumere la semplicità.
  • Code:
    • programmazione a coppie;
    • proprietà collettiva;
    • integrazione continua;
    • standard di codifica.
  • Test
    • test di unità continuo (da scriversi prima del codice);
    • test funzionale scritto dagli utenti nelle user stories.

In XP è inoltre presente la nozione di prototipo sotto il nome di spike, ovvero programmi molto semplici creati per esplorare soluzioni potenziali. Sono utili per capire se ho compreso le specifiche, la tecnologia da utilizzare e l’approccio da avere con i componenti esterni con cui bisogna dialogare. Questi prototipi vengono creati, mostrati al cliente e quindi scartati.

Documentazione

La documentazione cartacea non è necessaria: il cliente, il compagno di peer programming e il codice sono la documentazione.

La documentazione è sostituita dal codice in quanto:

  • i test di unità che sono delle specifiche eseguibili, infatti li scrivo prima di fare il codice (prima dico cosa voglio tramite il test);
  • il continuo refactoring consente di avere un codice estremamente leggibile e quindi elimina il bisogno dei commenti. Scrivere codice semplice tramite refactoring in modo che sia facilmente comprensibile è in realtà molto complesso.

CRC cards

Le Class Responsibility and Collaboration cards permettono di rappresentare classi e le relazioni tra di esse. Nate in ambiente didattico per spiegare l’OOP, sono ora utilizzati da alcuni team agile per discutere di design e il modo di utilizzo è simile a quello del planning game.

Le carte CRC sono realizzate su piccole schede di carta o cartoncino. Ciascuna carta descrive una classe (o un oggetto) in modo sommario, indicando:

  • Il nome della classe
  • Le sue superclassi e sottoclassi (dove applicabile)
  • Le sue responsabilità
  • Il nome di altre classi con cui questa classe collabora per svolgere i compiti di cui è responsabile
  • L’autore

L’uso di schede di piccole dimensioni ha lo scopo di limitare la complessità della descrizione, evitando che vengano riportate troppe informazioni di dettaglio. Serve anche a impedire che a una classe vengano assegnate troppe responsabilità. Il supporto cartaceo consente una serie di attività gestuali utili in fase di brainstorming, come piazzare le carte su un tavolo e spostarle, riorganizzarle, o anche eliminarle e sostituirle facilmente con altre nel corso della discussione. Il posizionamento delle carte su un tavolo può essere usato intuitivamente per rappresentare informazioni aggiuntive; per esempio, due carte possono essere parzialmente sovrapposte per indicare una relazione di stretta collaborazione, o una carta può essere posta sopra un’altra per indicare una relazione di controllo/supervisione.

Da Wikipedia, l’enciclopedia libera (licenza CC BY-SA 3.0).

Quando non utilizzare XP

Back non esclude mai la possibilità di utilizzare l’XP: secondo lui diceva può provare ad utilizzare questo approccio sempre (anche se in realtà non è sempre possibile “provare”), a patto che vengano rispettati i 12 punti elencati sopra.

Da questo possiamo concludere che Agile non si può usare quando:

  • l’ambiente non permette l’applicazione dei 12 punti, come per esempio succede con i team dislocati in luoghi diversi;
  • ci sono barriere managieriali, come team troppo numerosi;
  • ci sono barriere tecnologiche, come quando per esempio non è possibile utilizzare una macchina specifica condivisa da tutte le coppie per i test, ostacolando l’integrazione continua.
  • ci sono troppi stakeholders diversi e in contrasto tra loro;
  • situazioni in cui la consegna incrementale non ha senso, come per una centrale nucleare (vero Dyatlov?).

Critiche

Di seguito sono elencate alcune critiche all’eXtreme Programming fatte da Meyer (già pluricitato in questo documento).

  • Sottovalutazione dell’up-front, ovvero la progettazione iniziale prima di partire. Per Meyer, a parte in casi eccezionali (sviluppatori o manager particolarmente bravi) la progettazione non può essere fatta in modo totalmente incrementale. Nell’esperienza dei tesisti e colleghi di dottorando del prof. Bellettini questo problema non è così presente, ma potrebbe trattarsi di survivorship bias.
  • Sopravalutazione delle user stories: secondo Meyer sono troppo specifiche per sostituire i requisiti.
  • Mancata evidenziazione delle dipendenze tra user stories. Le user stories dovrebbero essere indipendenti tra loro, ma questo non è quasi mai possibile; nel design classico si utilizzano i diagrammi di Gantt per chiarire tutte le dipendenze tra i diversi punti del sistema da realizzare.
  • Il TTD può portare ad una visione troppo ristretta.
  • Cross functional team: se i team sono troppo disomogenei, ovvero ci sono tante singole figure specializzate in un campo e queste devono collaborare in coppia, ci possono essere dei problemi.

I punti di cui sopra cercano di evidenziare la mancanza di approfondimento e chiarezza dell’XP su alcuni aspetti dell’approccio ad un lavoro fornito da un cliente.

È consigliata la lettura del libro di Meyer.

Mesi uomo

È diffuso tra i manager pensare che la stima in tempo in mesi uomo sia graficata come un ramo di un iperbole, ovvero che il tempo diminuisca simil-esponenzialmente all’aumentare dei mesi uomo; tale stima non considera i tempi di overhead, ovvero il tempo impiegato per la comunicazione e tutto ciò che non è l’implementazione. I mesi uomo non quindi sono una metrica valida, ma sono utili solo a posteriori per valutare se un approccio ad un problema si è dimostrato valido.

Nella realtà, all’aumentare delle persone aumenta il bisogno di comunicare.

Quando il lavoro è strettamente sequenziale e non parallelizzabile (come la gravidanza) anche all’aumentare delle persone il tempo non cambia.

Nel mondo dello sviluppatore software spesso c’è un numero ideale di persone per un progetto; dopodiché, persone in più causano solo confusione e rallentano i tempi a causa della comunicazione. Il numero può anche essere grande, dipende dall’entità del progetto (esempio: space shuttle).

In generale, le metodologie agili iniziano a non funzionare più se il team è più grande di 8-10 persone. Quando il progetto non funziona più con un tale numero di persone, è necessario esplorare altre pratiche.