Software antifragile

L’imprevedibilità è una costante nel nostro mondo. Nello sviluppo software, come in molti altri campi, spesso chi ottiene i risultati migliori è chi riesce ad adattarsi a cambiamenti che apparivano impensabili finché non sono avvenuti. Chi eccelle in situazioni normali ma non è in grado di far fronte ad eventi eccezionali dovrà fare i conti, prima o poi, con qualcosa che butterà all’aria i suoi piani e che potrebbe avere ripercussioni gravose.

L’imprevedibilità è stata discussa in un celeberrimo recente libro di Nassim Taleb – “Il Cigno Nero”.

L’autore descrive come le nostre aspettative possano essere ribaltate da eventi totalmente inaspettati, e non solo da quelli che ritenevamo altamente improbabili, ma anche da quelli a cui non avevamo nemmeno pensato, tanto grande la loro peculiarità. Sebbene Taleb sia un esperto del mondo finanziario, il suo saggio descrive molteplici ambiti della vita e della società.
In seguito, Taleb scrive “Antifragile”, dove porta i suoi ragionamenti ad un’ulteriore domanda: come possiamo non solo gestire l’imprevedibilità, ma prosperare grazie ad essa? Possiamo cavalcare il caos per ottenerne il meglio? Inevitabilmente, gli informatici hanno iniziato ad adoperarsi per capire come questi principi si possano applicare allo sviluppo software.

Fragilità, robustezza, antifragilità

Innanzitutto, è necessario accettare che l’esito di un progetto software dipende anche da una componente di casualità. Per quanto facciamo il meglio, alcuni progetti daranno risultati migliori ed altri peggiori; d’altra parte, anche chi manca di doti e capacità ogni tanto potrà ottenere dei buoni risultati. Non accettare questo ci porta a false credenze ed aspettative irrealistiche, facendoci pensare di poter controllare tutto e che qualsiasi esito sia interamente attribuibile a nostri meriti o colpe.

La strada ben più saggia che possiamo seguire, invece, è quello di modellare come le probabilità del nostro guadagno o della perdita possono essere distribuite. Quando parliamo di guadagno o perdita, possiamo pensare in prima battuta ad una dimensione economica, in modo da avere una dimensione facile da capire e misurare. In generale però questa misura si può riferire a molti ambiti: formazione personale, buona nomea o pubblicità, soddisfazione individuale, e così via.
Vediamo ora alcune situazioni per quanto riguarda i possibili esiti di un progetto.

Probabilità ‘normale’

Molto spesso, le probabilità per eventi complessi si distribuiscono secondo quella che i matematici chiamano distribuzione normale, o un po’ meno formalmente campana di Gauss. Questo vuol dire che nella stragrande maggioranza dei casi l’esito sarà intorno ad un certo valore medio, ed in modo esponenzialmente meno probabile si allontanerà da questo valore per ottenere guadagni o perdite, tanto meno probabili quanto più ci si allontana dal valore medio.

In pratica nei progetti software: molto spesso guadagneremo da un progetto “qualcosa”, in alcuni casi meno frequenti guadagneremo molto o andremo in pari, ed in casi eccezionalmente rari perderemo molto o otterremo tantissimo.

Di fatto, molti sostengono che nell’ambito dei progetti informatici questo non succeda mai, perché è molto difficile avere progetti che possono ottenere sia risultati eccellenti che pessimi. Assumendo comunque che il successo del nostro progetto sia modellato da questa casistica, avremmo comunque gli stessi problemi di cui parleremo qui di seguito, a proposito dei progetti fragili.

Progetti fragili

Guardandoci intorno, noteremo che esistono invece dei progetti fragili. Immaginiamo dei casi in cui non possiamo eccellere, ma è molto probabile che riusciremo a ricavare un guadagno.
Supponiamo però che ci sia una piccola probabilità che le cose vadano male, anche molto male: potremmo non solo non guadagnare ma rimetterci cospicuamente. Oppure potremmo avere problemi di sicurezza dati che ci portano a gravi conseguenze. Oppure potremmo compromettere l’immagine che ci siamo costruiti così faticosamente, nel pubblico o per un determinato cliente.
Questo è quello che si dice un progetto fragile.

Pensandoci, è facile capire perché possano esistere progetti fragili: basta impostarli in modo da fare il minimo indispensabile per rendere un progetto presentabile, trascurando per esempio un’analisi delle necessità del cliente, un buon testing, e requisiti tecnici di sicurezza che difficilmente il cliente può verificare. Sul lungo termine, anche continuare a fare “quello che si è sempre fatto”, senza innovare o investire sulla propria formazione, ricade in questa casistica.
Potremmo avere un senso di sicurezza e stabilità, ma ipotizziamo che un domani cambino le necessità di nostri clienti abituali, o che usando nuove tecnologie la concorrenza riesca a soddisfarli meglio e più rapidamente: ci troveremo presto ad affrontare guai seri.

Il problema dei progetti fragili è che, se rischiamo grosso, quando le cose vanno male i problemi potrebbero estendersi anche agli altri progetti in porto – pensate a cosa può succedere a fronte di una grande perdita economica o di un grande danno di immagine. Peggio ancora, sul lungo termine una piccola probabilità di disastro su un singolo progetto cresce con il numero dei progetti che facciamo, facendo diventare l’emergere di una catastrofe qualcosa che, prima o poi, avverrà quasi sicuramente. Come direbbe l’ottimo Jacopo Romei, in parte ispiratore di questo articolo, la roulette russa alla lunga non è un business sensato.

Progetti robusti e resilienti

La prima soluzione si può mettere in atto per evitare processi fragili, è di adottare una serie di precauzioni per evitare gli incidenti peggiori. Un buon testing, duplicazione hardware per far fronte a guasti, attenzione a tutti i crismi per la sicurezza. Sono tutti ottimi principi, ma da soli non bastano ad eccellere – nel concetto di antifragilità si celano le opportunità di fare qualcosa di meglio.

Un sistema protetto da eventi catastrofici viene chiamato robusto a volte si parla anche di sistemi resilienti per casistiche che potrei in qualche modo definire come una via di mezzo tra i sistemi robusti ed antifragili.

Progetti antifragili

I progetti antifragili sono quelli dove siamo protetti da eventi catastrofici che potrebbero avere esiti nefasti, ma dove manteniamo una possibilità, magari anche piccola, di ottenere risultati brillanti che ci portino grandi benefici. Potremmo creare una nuova applicazione usata da milioni di utenti. Trovare una soluzione brillante per un cliente importante, che ci commissioni altri incarichi che ci consentono di espandere notevolmente la nostra attività. Potremmo ottenere una grande visibilità, che ci consentirà di rendere tutto più facile per gli anni a venire.

Notate che questi obiettivi non sono mai qualcosa che si può ottenere con certezza per un progetto. Spesso a posteriori è facile esaltare tutte le buone qualità di un prodotto di successo, facendone apparire la riuscita praticamente inevitabile. Si perde però di vista che tale successo è anche derivato da tante circostanze del momento – magari il prodotto è stato sviluppato in un momento particolarmente favorevole, oppure la concorrenza non ha brillato particolarmente, o ci sono state circostanze di mercato fortuite. Per quanto i presupposti siano buoni, i grandi successi non sono mai garantiti.

Nell’applicare l’antifragilità, la cosa importante è che noi abbiamo le potenzialità per ottenere dei risultati brillanti, in modo che se l’occasione si presenta, siamo pronti a coglierla e sfruttarla al meglio. Così come con approcci fragili sul lungo termine il disastro diventa sempre più probabile, se adottiamo approcci antifragili con coerenza e consistenza, a lungo andare le probabilità di risultati eccellenti diventeranno sempre più verosimili.

Come essere antifragili

É il momento di discutere su come alcune pratiche possano portare un progetto software ad essere antifragile. La lista che segue è tutt’altro che esaustiva, anche perché il concetto si estende a tantissimi aspetti del lavoro. Prendiamo ad esempio la gestione delle persone: se un vostro collaboratore all’improvviso non fosse più disponibile, sareste in grado di gestire il cambiamento o siete completamente bloccati?

In generale, il punto importante è chiedersi come si può applicare il concetto di antifragilità, e di essere in grado, caso per caso, di capire quali sono gli accorgimenti e le tecniche da mettere in atto in modo da massimizzarne i benefici.

Cicli di sviluppo con feedback

In molti processi Agile, il software viene rilasciato frequentemente, ed ai clienti vengono sottoposte le modifiche regolarmente in modo da consentire una comunicazione ottimale. Indipendentemente dalle modalità di sviluppo più o meno agili, è comunque importante fornire con continuità funzionalità di valore pratico per il committente (o per l’utente), e discutere i risultati in modo da avere un feedback costante.

Questo porta a molteplici benefici. In primo luogo si limitano i danni che possono avvenire se stiamo implementando qualcosa che non è di fatto utile – può succedere sia per incomprensioni per quanto riguarda i requisiti, ma anche nei casi in cui il cliente non ha un’idea chiara del prodotto che vuole finché non vede qualcosa con i suoi occhi. Consegnare un progetto dopo mesi di lavoro senza aver avuto alcun riscontro nel frattempo è un processo estremamente fragile: potrebbe essere molto lontano dalle aspettative e rivelarsi inutile o quasi. Con un feedback continuo, al massimo possono esserci piccole incomprensioni su piccole funzionalità in corso d’opera.

Discutendo regolarmente con il committente, è possibile che si individuino nuove funzionalità che con meno lavoro ci portano a risultati migliori. Potremmo persino abbandonare tutti i requisiti non ancora realizzati in favore di altri: abbiamo validato quello che abbiamo realizzato finora ha comunque un valore, quindi non abbiamo sprecato lavoro. Questa flessibilità ci consente di cogliere al volo occasioni dovute a eventi inaspettati, permettendoci di eccellere dove piani di lavoro più rigidi crollerebbero sotto il peso del cambiamento.

Testing automatizzato

Il testing automatizzato è una pratica che è cresciuta molto negli ultimi anni; in molti sistemi il costo dell’introduzione di bug ed errori nel codice può avere conseguenze gravose sul business. Anche in applicazioni meno delicate, avere dei test automatici che verifichino la correttezza di alcune funzionalità del codice ci aiuta a effettuare modifiche con sicurezza, minimizzando i rischi di regressione.

Per verificare che il sistema resista alle varie possibili problematiche, esistono anche delle tecniche dette di fault injection, cioè di introduzione volontaria di problemi all’interno del sistema, allo scopo di capire se il nostro programma è robusto a sufficienza da rilevare e reagire al meglio a questi problemi.

Tecniche di questo tipo ci consentono di prevenire problemi nel nostro software, anche dovuti a condizioni inaspettate spesso difficilmente prevedibili.

Pair programming

Il pair programming è una tecnica che consiste nel programmare a coppie – una persona scrive codice, mentre l’altra controlla che quanto scritto sia giusto e soprattutto tiene presente la visione di insieme, guidando il compagno più focalizzato sul dettaglio. I due ruoli si scambiano ad intervalli regolari.

Sebbene due programmatori alla stessa tastiera possa sembrare un investimento di tempo molto grande, la qualità del codice prodotto può essere notevolmente migliore. Dal punto di vista della fragilità, è molto difficile che si verifichino grossi bug, visto che ogni riga di codice viene controllata da due persone nel momento stesso in cui viene scritta. Il pair programming può essere una tecnica valida soprattutto per portare avanti parti del programma particolarmente complesse e delicate.

Modularità, scalabilità, espandibilità

Queste tre qualità possono essere riferite sia al codice prodotto, sia all’infrastruttura hardware che lo supporta. Insieme, supportano l’antifragilità in modo simbiotico. La modularità ci consente di separare la nostra applicazione in parti coese ma disaccoppiate, in modo che il fallimento di una non si propaghi sulle altre. La scalabilità e l’estensibilità ci consentono di far crescere con facilità il programma, dal punto di vista delle funzionalità e del carico di lavoro (es. numero di utenti supportati).

Da un lato queste qualità ci consentono di arginare eventuali problemi; dall’altro ci permettono essere in grado di fornire rapidamente nuove funzionalità e risorse, rispondendo efficacemente alle richieste di mercato in caso l’applicazione riscontri, anche improvvisamente, un grande successo.

La killer feature

Torniamo ad una parte più concettuale. Come informatici, quando si ha davanti una lunga lista di funzionalità da implementare entro una scadenza che pare sempre troppa vicina, si rischia di perdere la visione di insieme. A volte, lo stesso obiettivo potrebbe essere realizzato con molto meno, con poche funzionalità mirate per qualche motivo molto più efficaci di tutto quello che abbiamo progettato. Talvolta, i più grandi successi sono determinati da poche, semplici caratteristiche, e non da migliaia di funzionalità. Viene quindi naturale chiedersi se possiamo trovare una killer feature, un qualcosa di brillante e innovativo che ci consenta di avere un grande impatto, sia questo su un committente, sugli utenti finali o sui processi interni del nostro team.

Se vogliamo essere antifragili, ed avere la possibilità di eccellere anche a fronte del cambiamento, dobbiamo cercare attivamente occasioni di questo tipo. Un primo passo può essere quello di condividere gli obiettivi e la visione a lungo termine con colleghi e collaboratori – come sottolineano molti agilisti, avere chiaro il perché stiamo facendo qualcosa è fondamentale. Chiedersi regolarmente se quello che abbiamo in programma è la strada più breve per l’obiettivo può essere un secondo passo. Retrospettive e brainstorming sono un ottimo strumento a supporto di questo obiettivo. A seguire, flessibilità e volontà di sperimentare ci aiutano per capire se le nostre idee possono funzionare: siamo disposti ad abbandonare i nostri piani per provare a cogliere un’occasione o seguire una valida idea? Siamo antifragili a sufficienza da non avere gravi ripercussioni se l’idea si rivela non così buona?

Sviluppare un’idea che dia una svolta radicale ad un progetto, creare un qualcosa di nuovo ed innovativo, lanciare un’applicazione di grande successo: questi sono obiettivi che richiedono di avere delle idee a cui finora nessuno ha pensato finora e di metterle in atto. Non ho, chiaramente, la pretesa di spiegare come raggiungere questi obiettivi; quello che mi preme sottolineare è come, per essere antifragili ed avere la possibilità di ottenere il massimo, occorre tenere presente e perseguire questi obiettivi attivamente, cercando sempre nuovi spunti e possibili miglioramenti. A volte scopriremo che quello che stiamo facendo è difficilmente migliorabile, e che non si può fare molto di più; quando però avremo una possibilità di grande crescita, o di cogliere un’occasione unica a fronte di cambiamenti inaspettati, l’antifragilità ci consentirà di dare il meglio e sfruttare tutto il potenziale che abbiamo a disposizione.