Come raggiungere un flusso di lavoro ottimale per lo sviluppo software in team.

Nel nostro team di sviluppo software abbiamo costruito, nel tempo, un flusso di lavoro che ci ha permesso di raggiungere ottimi risultati, con minor sforzo. Gli sviluppatori sono più coinvolti e motivati, e lavorano meglio. Ecco come abbiamo raggiunto questi risultati.

Also available in English.

Questo articolo fa parte di una serie in cui abbiamo cercato di raccontare l’intero flusso di lavoro del team di sviluppo:

  • Come raggiungere un flusso di lavoro ottimale per lo sviluppo software nel team: introduzione al flusso, principi e pratiche
  • Diventare agili: come introdurre la metodologia scrum in un team e applicare il cambiamento all’azienda
  • Lo sviluppo Web a componenti: focus dettagliato sull’adozione di un sistema di componenti software per lo sviluppo Web in un team Agile (Work in Progress)
  • La Continuous Integration in un team Agile: come rendere affidabile e veloce il flusso di lavoro e di release dei progetti (Work in Progress).

Introduzione al flusso

Cosa è possibile ottenere con un flusso collaudato di sviluppo software collaborativo

Sono un Ingegnere del Software e lavoro attualmente in Chialab. Nel nostro team contiamo circa 10 sviluppatori che lavorano su progetti molto eterogenei, da web-app a CMS e mobile app. Spesso gli sviluppatori lavorano simultaneamente su diversi progetti, cambiando contesto anche diverse volte al giorno.

Il flusso attuale di sviluppo software ci ha permesso di gestire i nostri progetti raggiungendo:

  • Migliori risultati
  • Una migliore conoscenza della codebase e delle tecnologie
  • Una migliore conoscenza e intesa fra i membri del team
  • Soddisfazione personale, minor frustrazione ed un maggior senso di appartenenza ai progetti.

Ci organizziamo in maniera autonoma lavorando con metodologia Agile Scrum. Lavorare in un team agile porta grandi benefici ma richiede il rispetto di principi e pratiche, sia generali del movimento Agile che specifiche del framework Agile adottato. Per approfondire come introdurre un approccio agile al team, si veda l’articolo Diventare agili.

Un flusso incrementale e iterativo

Costruire un flusso rodato e funzionante di sviluppo software non è qualcosa che si ottiene in poche settimane. Richiede tempo, poiché è un processo ciclico. Come ci insegna lo sviluppo software Agile, tale processo può essere definito sia incrementale che iterativo.

Con “incrementale” ci riferiamo all’aggiunta progressiva di features al prodotto, in ogni Sprint (in Agile, periodi di lavoro temporalmente definiti). Entrando nel merito, abbiamo cominciato ad adottare un sistema software a componenti (Component-based software) che, tra i tanti noti benefici, porta con sé il vantaggio di applicare più facilmente questo processo al software; ci permette di avere un insieme di “mattoncini” (i componenti software) che possono essere sviluppati e riusati indipendentemente dallo sviluppo del sistema software principale.

Il concetto di “iteratività” del flusso di lavoro è intrinseco nella definizione di Sprint in Agile. Al termine di ogni periodo definito di lavoro, riceviamo feedback da clienti e tester. Questo ci aiuta nel comprendere le priorità delle issue (che spesso cambiano in corso d’opera), così da capire quali funzionalità sviluppare prima e quali rimandare o eliminare poiché obsolete. Anche in questo caso, lo sviluppo a componenti ci permette di avere una granularità più fine e quindi di aggiungere/rimuovere funzionalità con uno sforzo decisamente ridotto rispetto a quello che si avrebbe con uno sviluppo monolitico.

Agile Retrospectives

Di grande impatto per il miglioramento del flusso di lavoro sono le Agile Retrospectives. Questi momenti rappresentano un metodo efficace e costruttivo per discutere vari aspetti; è il contesto nel quale discutiamo problematiche interne al team ed esaminiamo attentamente il flusso stesso, riguardo all’esperienza avuta con lo Sprint appena concluso. L’analisi retroattiva è fondamentale per un miglioramento iterativo del team, per questo andrebbe comunque effettuata anche se non si segue una metodologia Scrum, provando a definire dei momenti periodici di discussione critica sull’ultimo periodo di lavoro.

Alcune regole fondamentali da osservare durante questi momenti di discussione ed analisi, sono:

  • Trasparenza durante le discussioni
  • Evitare di discutere aspetti tecnici e tecnologici durante le Retrospective. Come vedremo in seguito, le Code Review sono il momento riservato a tali discussioni
  • Non rimandare nessuna discussione: i problemi non affrontati si ripresenteranno allo stesso modo, o in maniera amplificata, nella prossima Retrospective.

Analizziamo ora le varie fasi del flusso, entrando nello specifico degli strumenti coinvolti con cui abbiamo avuto esperienza diretta nel nostro team.

Il flusso nel dettaglio

Fase 1 — Scrittura della issue, stima ed assegnazione allo sviluppatore

La fase di scrittura della issue (un’unità di lavoro assegnata ad uno sviluppatore) può essere effettuata da un Product Owner, dallo Scrum Master o da uno sviluppatore, non è importante; ciò che conta è che la descrizione sia comprensibile tanto agli sviluppatori quanto alle figure meno tecniche. Il linguaggio utilizzato deve risultare sempre comprensibile ed essere un mix di aspetti tecnici e funzionali.

Una issue d’esempio potrebbe essere: “realizzare una maschera di input contenente i campi username e password, che in caso di errore nel login mostri il messaggio “Errore”. Vedere immagini in allegato per la realizzazione grafica”. È consigliato allegare più informazioni possibili quali commenti, immagini e documenti utili allo sviluppatore per realizzare la issue nella maniera più fedele possibile ai requisiti iniziali.

La issue va poi stimata, collegialmente dal team. Questa è una fase fondamentale del flusso; è importante che tutto il team partecipi alla stima. Abbiamo raggiunto una buona capacità nello stimare basandoci sui seguenti aspetti:

  • Difficoltà intrinseca della issue
  • Non conoscenza del contesto o della tecnologia
  • Probabilità di insorgere di problemi imprevisti
  • Tempo necessario per realizzare i dovuti test e per la review del lavoro.

In Agile ci sono diverse metriche utilizzate per la stima delle issue. Tuttavia, come in tanti aspetti di Agile, l’utilità effettiva e l’efficacia di ogni pratica è da valutare per lo specifico team, iterando nei vari Sprint e provando diverse metriche fino a trovare quella con cui il team riesce ad avere i risultati più consistenti.

Per le stime si possono utilizzare Story Points (definiti dalle caratteristiche elencate poco sopra) oppure le ore di lavoro; nel primo caso i possibili valori assegnabili come stima possono essere numeri della serie di Fibonacci o taglie di indumenti (S, M, L, ecc.). L’importante è trovare, col tempo, ciò che meglio funziona per il team.

Come si diventa bravi nello stimare? Grazie ai feedback degli Sprint passati. Analizzare le stime di issue precedenti aiuta a migliorare la capacità del team nelle nuove stime. È fondamentale, per questo, adattare le stime che sono state inizialmente errate, anche durante lo Sprint, onde evitare che un’analisi del pregresso risulti fuorviante.

Come team abbiamo riscontrato notevoli miglioramenti nella stima delle issue, adattandola allo sviluppatore. Una volta effettuata l’assegnazione, il singolo sviluppatore analizza la stima iniziale e propone di mantenerla o modificarla, sulla base delle proprie conoscenze relative alla issue (quindi alla tecnologia, alla conoscenza del progetto, ecc.). Ogni sviluppatore ha le proprie peculiarità e una maggiore esperienza su alcuni progetti piuttosto che su altri: adattare la stima al singolo sviluppatore, col tempo, ci ha permesso di avere un carico di lavoro ideale del team per Sprint (velocity) definito in maniera consistente.

È importante sottolineare che se una issue è assegnata ad uno sviluppatore questo non significa affatto che verrà completata esclusivamente dallo stesso; l’assegnazione rappresenta soltanto una presa di responsabilità dello sviluppatore nel portare a termine il compito. Questi potrà essere aiutato (come spesso accade) da diversi sviluppatori, con un maggior coinvolgimento e senso di appartenenza dei membri del team verso il progetto (questo principio, oltre che essere guidato dal buon senso, è esplicitato come un valore dello Sviluppo Software Agile).

Fase 2 — Sviluppo software

Uno strumento ormai fondamentale nello sviluppo software sono i sistemi di versionamento (ad esempio, Git). Essi ci permettono, tra le altre cose, di mantenere un flusso di lavoro separato per le diverse issue in lavorazione, avendo contemporaneamente diverse versioni remote (e controllate) della codebase, i cosiddetti branch. Tali branch permettono ad ogni sviluppatore di lavorare prima in una copia locale del sistema software, aggiornata agli sviluppi relativi alla propria issue, per poi richiedere che la propria modifica venga propagata alla versione principale del branch (in genere, un branch denominato master).

Nel nostro team, oltre ad un branch principale, abbiamo introdotto branch specifici per gli Sprint. Lo Sprint rappresenta in genere un periodo al termine del quale vengono “rilasciate” delle modifiche visibili al cliente. È importante mantenere attive contemporaneamente diverse istanze dello stesso sistema software, per isolarle dalle diverse modifiche specifiche per istanza e da eventuali fallimenti, e che rispecchino le varie fasi del ciclo di vita del software (sviluppo, staging — detta anche “test” — e produzione).

Il branch legato allo Sprint viene utilizzato come versione principale durante lo sviluppo intra-Sprint, per poi allineare la copia principale (master) ad esso dopo la demo di fine Sprint. Il branch di uno Sprint appena concluso viene poi cancellato e viene creato il nuovo branch, per lo Sprint successivo.

Fase 3 — Validazione

Lo sviluppatore, una volta completata la issue, chiede che venga propagata al branch principale, tramite una Merge Request. Le Merge Request vengono adottate da diversi sistemi quali Gitlab, che utilizza un flusso denominato “GitFlow”, e similarmente anche da Github con il nome di Pull Request (il flusso è leggermente diverso, si veda questo link per approfondimenti).

Nel nostro caso, il sistema usato è Gitlab. Due strumenti di questa piattaforma ci aiutano in questa circostanza:

  • Le Milestone di Gitlab possono venire utilizzate per “etichettare” le singole Merge Request, in modo da essere facilmente rintracciabili. È così possibile visualizzare rapidamente, ad esempio, le Merge Request aperte in un dato momento, riguardo tutti i progetti, nello Sprint corrente. Le Milestone utilizzate sono rappresentate dal codice dello Sprint in corso.
  • Le etichette (label) di Gitlab aiutano a colpo d’occhio a fornire informazioni sullo stato di una MR (Merge Request), senza aprirne il dettaglio.

Se la MR viene accettata, la modifica viene propagata sul branch principale, pronta per essere rilasciata.

Ogni issue deve prima però passare due test: uno di tipo tecnico, la cosiddetta Code Review, e un’accettazione di tipo funzionale, spesso operata dal responsabile della Quality Assurance del progetto.

Validazione funzionale

In questa fase di validazione non va analizzato il codice bensì il comportamento ad alto livello dell’applicazione. Ciò che è soggetto a verifica sono soltanto i requisiti funzionali previsti per la issue.

In genere tale validazione va effettuata per prima, poiché è più probabile che un errore nel comportamento dell’applicativo, oppure un fraintendimento dei requisiti, porti a un cambiamento significativo nella codebase rispetto ad uno o più errori notati nella fase di Review (come vedremo, questa assunzione non è una regola, e la flessibilità è importante durante il flusso).

Code Review

In questa fase avvengono le discussioni di carattere tecnico riguardo le modifiche introdotte per la issue in questione. Si hanno due figure in campo, il reviewee (colui che ha completato il lavoro ed ha “aperto” la Merge Request) e il reviewer (colui che analizza il codice e le modifiche introdotte, per poi “chiudere” la MR). Il reviewer analizza il lavoro sotto l’aspetto tecnico, cominciando discussioni sulle specifiche parti di codice tramite la possibilità di commentarle in-line nella maschera offerta da Gitlab. Soltanto quando tutte le discussioni vengono dichiarate “risolte” dal reviewer, la MR viene data per accettata.

Online è possibile trovare diversi articolo riguardo alle “best practices” per le code review (ad esempio: https://medium.com/@palantir/code-review-best-practices-19e02780015f), sia da un punto di vista del reviewer che del reviewee. Quello che vorremmo riportare qui, sulla base della nostra esperienza, sono gli accorgimenti che più hanno contribuito positivamente al nostro flusso di lavoro:

  • Usare toni cordiali e comprensivi: un reviewee è psicologicamente più incline a una discussione sul “proprio” codice se la discussione è posta con termini cordiali anziché sentirsi “attaccato” con segnalazioni sbrigative e superficiali
  • Proporre soluzioni invece di “puntare il dito” e semplicemente notificare la presenza di un errore
  • Aprire discussioni anche soltanto per proporre un proprio punto di vista o una soluzione alternativa, o più semplicemente per chiedere chiarimenti: la code review non è soltanto un momento di analisi ma soprattutto un momento di crescita per il team
  • Coinvolgere altri membri del team per ulteriori pareri
  • Non aver “timore” di segnalare ogni problema: è importante essere estremamente precisi e puntuali nel segnalare anche i più piccoli typo. Errori trovati in code review ma non segnalati, verranno propagati alla versione principale del software, creando frustrazione per gli sviluppatori che lavoreranno in seguito sulla codebase
  • Essere flessibili: è importante anche non esagerare con la segnalazione di errori identici e ripetuti. Spesso certi errori vengono ripetuti allo stesso modo in diverse parti di codice; in questi casi è utile segnalare ovunque l’errore ma evitare, magari, di ripetere per ogni caso la spiegazione data in primis.

Sottoporre il codice a code review permette di ottenere una codebase che:

  • È il risultato di un lavoro collegiale
  • È il più possibile compliant con standard intra-team, quali documentazione, test, e regole di formattazione del codice
  • È il più possibile conosciuta equamente dal team, facendo sì che ogni singola modifica non venga mai propagata alla versione principale senza che almeno due sviluppatori l’abbiano prima analizzata.

Conclusione

Il flusso di lavoro è dato dal contributo di ogni membro del team: per questo la sua efficacia è legata all’esperienza maturata dal team stesso. È molto importante utilizzare Retrospectives, Code Review e ogni tipo di meeting intra-team per analizzare il flusso ed avere un atteggiamento critico, per potersi migliorare ad ogni Sprint.

È altresì importante sfruttare gli strumenti offerti, come quelli già citati, molti dei quali gratuitamente disponibili online, e di non fermare mai la sperimentazione e la ricerca di nuove metodologie. Così come il mercato tecnologico cambia, anche i metodi di sviluppo devono stare al passo con le nuove esigenze date dai clienti e dai cambiamenti tecnologici.

L’aspetto fondamentale sta nel trovare un flusso che sia condiviso ed accettato da tutti i membri del team e non avere timore di cambiare subito alcune scelte quando le cose non iniziano a funzionare come ci si aspettava: il cambiamento, in Agile, è sempre consigliato, se adottato gradualmente e in maniera iterativa.