Metodologie Agili

I metodi agili si propongono di sviluppare software che abbia un alto valore per il cliente, rilasciandolo presto e a ritmo costante.

Un tipico team agile già dopo un paio di settimane rilascia una prima versione del software, incompleta ma già dimostrabile. In questo modo il cliente ha un immediato feedback sulla qualità del software consegnato, e può a sua volta fornire agli sviluppatori un prezioso feedback, per mantenerli focalizzati su quello che al cliente serve veramente. Se il team XP è bravo, riesce a mantenere un ritmo costante di rilasci frequenti, tipicamente ogni una o due settimane.

I metodi agili condividono alcuni principi e valori fondamentali, descritti nell’Agile Manifesto. I più diffusi sono Extreme Programming e Scrum. L’approccio di Claranet è basato su Extreme Programming (XP), con l’aggiunta di alcune pratiche prese a prestito da Scrum.

Presto e a ritmo costante Quanto tempo è necessario perché dall’idea di un progetto si ottengano profitti dalla realizzazione di questo progetto? Se tracciamo le attività coinvolte abbiamo

  • studio di fattibilità
  • analisi
  • progettazione
  • codifica dei moduli
  • integrazione
  • test
  • installazione

Anche assumendo che ciascuna di queste attività richieda solo due settimane, e non ci siano né pause né rilavorazioni, ne dobbiamo dedurre che non è possibile ottenere profitti da un’attività di sviluppo software in meno di 14 settimane. Nella realtà delle cose, i tempi per queste attività sono molto maggiori. Il problema è che queste fasi vengono realizzate in sequenza, e non c’è modo di ottenere valore dal progetto prima della fase finale in cui viene installato.

Un altro problema è che è difficile avere una misura concreta di quando il software sarà pronto, fino a quando lo sviluppo non è quasi concluso. È nella fase di integrazione che si nascondono le sorprese. Allo stesso modo, fino a quasi alla fine dello sviluppo non abbiamo una misura concreta della qualità del software che otterremo.

La risposta dei metodi agili a questi problemi è nello sviluppo incrementale ed evolutivo. Il software viene realizzato con un ritmo costante di iterazioni brevi, della durata di una o due settimane. Ogni una o due settimane, il team consegna il software che contiene le feature realizzate fino a quel momento. Il sistema è incompleto, nel senso che non fa tutto quello che deve fare; ma le feature che ci sono hanno la qualità finale, ovvero sono state implementate al livello di dettaglio e di qualità sufficiente per metterle in produzione.

Quindi il committente può decidere, ad ogni iterazione, se le feature consegnate sono sufficienti per andare in produzione. Si può decidere di andare in produzione in anticipo, e installare le feature mancanti in un secondo tempo. In questo modo il progetto inizia a produrre profitti prima di essere completo.

Ma non solo; ad ogni iterazione il committente può toccare con mano la qualità del lavoro fatto finora, sia dal punto di vista della robustezza, che della rispondenza ai desiderata. Riusciamo così a scongiurare il pericolo di consegnare on time and on budget solo per sentirci dire che “non era questo quello che volevo”!

Il ritmo delle iterazioni è anche un’occasione per il cliente per cambiare idea. I requisiti cambiano a seconda del mutare delle condizioni di mercato, e anche a seconda del progredire della comprensione da parte del committente di quello che effettivamente desidera, di quello che effettivamente ha valore. Il team agile non ha bisogno di conoscere in anticipo tutti i requisiti, ma è in grado di accettare requisiti nuovi e imprevisti in qualsiasi momento. Sia che i nuovi requisiti riguardino funzionalità ancora da realizzare, che funzionalità già realizzate, il costo del cambiamento non è esorbitante.

Quindi le attività di analisi, progettazione, codifica, test e integrazione restano necessarie; ma un team agile non le realizza come fasi separate in sequenza. Ciascuna iterazione comprende una parte di lavoro in ciascuna di queste attività, che vengono realizzate quasi in parallelo. L’abbattimento della barriera temporale costituita dalla fasi consente di aumentare la produttività.

Il costo del cambiamento Guardiamo ora la questione da un’altro punto di vista: preso un software esistente qualsiasi, quanto tempo ci vuole per ottenere che una richiesta di modifica venga messa in produzione? Una frase che si sente dire frequentemente è “come mai ci vuole così tanto tempo per una modifica così semplice”?

I metodi agili si basano sulla scommessa che sia possibile rendere falso l’assunto secondo cui una modifica costa proporzionalmente di più a seconda di quanto tardi nello sviluppo venga richiesta. Il software engineering tradizionale sostiene che una modifica richiesta in fase di testing costi un ordine di grandezza di più rispetto a una modifica in fase di codifica, e due ordini di grandezza in più rispetto a un modifica in fase di design, e così via.

Al contrario, i metodi agili presuppongono che sia possibile mantenere più o meno costante il costo delle modifiche su tutto l’arco di vita di un software, dall’analisi alla codifica, al rilascio, alla manutenzione.

Perché questo possa essere vero, i metodi agili presuppongono un processo diverso rispetto al processo tradizionale, sia dal punto di vista delle pratiche ingegneristiche di scrittura del codice, che dal punto di vista del design, che dal punto di vista della pianificazione e organizzazione del lavoro.

L’organizzazione del lavoro

Il cliente presente Il viaggio di un progetto agile inizia con la definizione di un cliente che ha la responsabilità di rappresentare gli stakeholder di progetto nel definire la funzionalità che dovrà essere realizzata. Il “cliente” così identificato spesso non è l’unico vero cliente del sistema; ma è importante che si centralizzi in una persona l’onere di definire quali funzionalità vanno realizzate, e in quale ordine di priorità.

Lo sviluppo agile si basa su una separazione di ruoli e responsabilità. Il cliente definisce funzionalità e qualità del prodotto; il team si auto-organizza per realizzare il prodotto, ed è responsabile delle scelte tecniche.

La pianificazione

La pianificazione di un progetto agile si basa sulla separazione delle funzionalità in storie d’uso o user stories, che sono frammenti di funzionalità utilizzabili dall’utente. L’analisi del problema conduce alla definizione di un certo numero di storie, ciascuna delle quali

  • Ha un senso per l’utente, nel senso che il committente è in grado di verificarla
  • Produce valore, nel senso che è evidente al committente che il sistema vale di più con questa storia piuttosto che non avendola
  • È stimabile, nel senso che il team è in grado di stimare quanto lavoro sarà necessario per realizzarla.
  • È realizzabile in un tempo limitato, tipicamente da una a tre giornate.

Stime di complessità

Gli sviluppatori assegnano una stima di complessità a ogni storia. La stima è in “punti” astratti. Questi punti rappresentano la difficoltà di realizzare una storia, relativamente ad altre storie. Ad esempio, se la storia A è stimata 2 punti e le storia B e C sono stimate 1 punto ciascuna, possiamo attenderci che sviluppare A costi più o meno quanto sviluppare B e C.

Prioritizzare

Il cliente sceglie l’ordine in cui le storie devono essere realizzate. In questo modo verranno affrontate per prime le storie che rappresentano per il cliente il valore maggiore.

Iterazioni e velocità

Un’iterazione dura una o due settimane.

In questo periodo gli sviluppatori completano le storie più importanti, secondo l’ordine di priorità specificato dal cliente. Alla fine dell’iterazione il cliente può verificare sul sistema il funzionamento delle storie che egli stesso ha chiesto.

Quante storie riescono a realizzare gli sviluppatori in un’iterazione? Dipende da tanti fattori. Quello che sappiamo è che a regime, gli sviluppatori riescono a realizzare un numero di punti complessità più o meno costante. Si misura che in una tipica iterazione si riescono a sviluppare storie per un totale di, poniamo, 12 punti complessità. Questo numero è detto la velocità del team. La velocità non serve a misurare la produttività del team; serve a stimare quante storie possiamo attenderci di realizzare in un’iterazione.

Le pratiche ingegneristiche agili

Simple design

Lo strumento fondamentale per mantenere basso il costo del cambiamento è la manutenzione di un’architettura semplice. In un progetto agile di successo, la complessità di inserire una nuova funzionalità non aumenta, anzi diminuisce con il tempo, poiché il progetto evolve verso un’architettura che rende semplice inserire il tipo di modifiche che vengono effettivamente richieste.

Come si ottiene un’architettura semplice? La prima cosa è un uso corretto della programmazione ad oggetti. Nella pratica comune tutti i programmatori usano linguaggi a oggetti come Java o C#; molti conoscono e usano i design patterns; e tuttavia spesso si vedono progetti che sono realizzati per mezzo di decomposizione procedurale, rivestita da un sottile strato di oggetti. Non che ci sia qualcosa di male nella programmazione procedurale in sè; può funzionare bene nel software engineering tradizionale, ma non conduce al tipo di architettura duttile che è necessario per i metodi agili.

Idealmente, nel codice di un progetto agile, ogni classe ha una sola responsabilità, e contiene metodi di complessità ciclomatica compresa fra 1 e 2, e di lunghezza di 3 o 4 righe, con punte di 7 o 8.

Il secondo strumento fondamentale per ottenere l’architettura semplice è il refactoring.

Refactoring

Il refactoring è la pratica di migliorare l’architettura del codice esistente in maniera sicura. Lo sviluppatore applica delle mosse di refactoring per migliorare il design.

Si tratta quindi di migliorare la struttura interna del codice senza aggiungere funzionalità; è una pratica dedicata a combattere la naturale entropia di tutte le basi di codice.

È anche un gesto di realismo e umiltà, perché si accetta di avere scritto codice dall’architettura non ideale, e lo si trasforma in codice migliore. È l’opposto della tentazione sempre presente di chi dice “ah, se potessimo riscrivere il sistema da zero, allora sì che lo faremmo bene”. Il realismo ci dice che se il codice che abbiamo è disordinato, difficilmente riscrivendolo da zero otterremmo un codice molto migliore. Quindi il refactoring è una serie di piccole modifiche, non una grande riscrittura.

Il refactoring infine deve essere effettuato in maniera sicura. Il vecchio adagio ingegneristico “non toccare quello che funziona” ha un senso; non vogliamo rischiare di introdurre errori nel codice funzionante per l’ambizione di perfezionarlo. Questa sicurezza si ottiene grazie a una rete di test unitari automatici, che sono ottenuti come sottoprodotto del test-driven development.

Test-Driven Development

Il TDD è una pratica di progettazione, che consente di fare in modo che ogni riga di codice del sistema sia stata progettata in seguito al fallimento di un test unitario automatico. Lo sviluppatore prima scrive un test, che specifica il comportamento del sistema in un particolare scenario. Dato che il test viene scritto prima del codice di produzione, inizialmente il test fallirà. Poi lo sviluppatore scrive il codice di produzione che permette al test di passare. Non solo l’ultimo test, ma tutti i test unitari del sistema devono passare; questo impedisce allo sviluppatore di commettere l’errore di fare progressi su un fronte e rompere la funzionalità esistente altrove.

Quando abbiamo tutti i test unitari funzionanti, la disciplina del TDD impone di applicare tutto il refactoring necessario per riportare il sistema nella forma più semplice, che permetta di fare passare tutti i test. Quando lo sviluppatore è soddisfatto, scrive il test successivo, e il microciclo ricomincia.

Questo è il microciclo del TDD. Si procede per piccoli passi; si scrive un piccolo test, si aggiunge la poca funzionalità necessaria per farlo passare, si fa tutto il refactoring che appare necessario. Lavorando per piccoli passi si commettono solo piccoli errori.

Il TDD ha quindi come obiettivo di ottenere la migliore architettura possibile. Come utili sottoprodotti, abbiamo che

Il TDD produce una suite di test unitari automatici che ci proteggono contro le regressioni, e ci consentono di applicare il refactoring con la sicurezza di non rompere nulla.

Il codice prodotto da un team che applica il TDD è in larga misura privo di errori. So che questa è un’affermazione forte da fare, ma corrisponde alla nostra esperienza in Claranet. L’attività di debugging è quasi completamente scomparsa.

È chiaro che il TDD è una pratica che richiede una certa disciplina. Per aiutare gli sviluppatori a mantenere questa disciplina, è utile la pratica del pair programming.

Pair Programming

Il pair programming consiste nel mettere due sviluppatori fianco a fianco, su un’unica postazione di lavoro, per realizzare il codice di una feature. Il postulato su cui questa pratica si fonda è che l’attività di codifica è essenzialmente un’attività di pensiero, e che ragionando in due la qualità del codice prodotto sarà molto migliore.

L’attività di codifica è molto delicata, perché agisce sul patrimonio più importante di un team di sviluppo, che è la base di codice. Come non è pensabile di lasciare un chirurgo a operare da solo, alla stessa maniera è importante avere due paia di occhi ad osservare quali modifiche vengono applicate sul codice.

Il pair programming è anche un importante antidoto contro il pericolo, molto reale nei progetti software, che ci siano aree grigie nel codice che sono capite solo dallo sviluppatore che le ha scritte. Spesso queste aree grigie coincidono con i moduli che producono più errori.

Questa pratica è una versione estrema di “code inspection”, e ha come effetto collaterale di consentire ai programmatori junior di progredire rapidamente al livello dei senior. Un altro effetto collaterale è permettere la proprietà condivisa del codice.

Proprietà condivisa del codice

Un team agile è più della somma dei suoi componenti, perché i membri del team si organizzano per lavorare insieme in maniera efficace. Una componente importante di questa efficacia è il remare tutti insieme nella stessa direzione. Si rende necessario quindi un pensiero condiviso, un’idea di base condivisa su come deve essere scritto il codice; questo va al di là delle convenzioni di stile e arriva a comprendere le scelte architetturali.

Un team agile maturo quindi non considera il codice come una collezione di provincie private, ma lo considera un patrimonio condiviso, un giardino comune da irrigare e arricchire. La regola è che ciascun membro del team è autorizzato a modificare il sistema in un qualsiasi punto.

Conclusioni

In questo breve documento abbiamo parlato di alcuni degli aspetti più importanti dei metodi agili. Non c’è stato spazio per parlare di come il team si auto-organizza o di come si mette in pratica il miglioramento continuo. Spero che emerga come i metodi agili siano processi di sviluppo basati su alcuni valori centrali: l’umanità, il coraggio, la trasparenza, la competenza tecnica, la qualità, il feedback, la collaborazione.

La scommessa dei metodi agili è che adottando metodi di sviluppo centrati sugli esseri umani e sulla loro natura, sia possibile ottenere i migliori risultati. I metodi agili giocano per vincere.


Riferimenti

  • [The Agile Manifesto](http://agilemanifesto.org/)
  • Kent Beck, Test-Driven Development By Example, Addison-Wesley, 2002.
  • Kent Beck, Extreme Programming Explained, 2nd edition, Addison-Wesley, 2005.
  • Frederick Brooks, The Mythical Man-Month after 20 Years, in The Mythical Man Month, Anniversary Edition, Addison-Wesley, 1995.
  • Mike Cohn, Agile Estimating and Planning, Prentice-Hall, 2006.
  • Martin Fowler, Refactoring: Improving the Design of Existing Code, Addison-Wesley, 1999.
  • [Ron Jeffries, A metric leading to agility](https://ronjeffries.com/xprog/articles/jatrtsmetric/)
  • [Robert Martin, The Big Redesign In The Sky](https://web.archive.org/web/20121013052815/http://blog.objectmentor.com/articles/2009/01/09/the-big-redesign-in-the-sky)