Perché lo sviluppo e la manutenzione di un prodotto o un servizio digitale sono sempre costellati di bug e problemi? E chi li deve pagare? Il committente o il team di sviluppo?
In questa puntata del CTO Podcast rispondo ad entrambe le questioni, risolvendo una volta per tutte l’equivoco di fondo che genera confusione su questo argomento.
Trascrizione
Dietro le quinte, il lavoro di un team di sviluppo e di gestione dell’operatività di un prodotto digitale è caratterizzato da molte soddisfazioni così come da molti problemi frustranti.
Dal punto di vista delle soddisfazioni, una di quelle che personalmente mi stimolano di più è quella di poter creare, spesso da zero, qualcosa che genera un risultato anche molto rilevante per un cliente.
Tra le frustrazioni ci sono invece quelle relative al fatto che è impossibile produrre software senza difetti o riuscire a servire sempre correttamente una richiesta operativa di un utente, anche con le migliori intenzioni, così come riuscire a far sì che un’infrastruttura abbia un uptime del 100% in un anno è pressoché infattibile e, in ultima istanza, anti economico.
In generale molti di questi problemi rimangono poco visibili agli utenti finali e ai committenti, se il team è ben organizzato e ci sono dei processi di controllo adeguati.
Tuttavia, proprio per la natura del software e dei sistemi che lo fanno girare su Internet, e in generale della complessità di una piattaforma digitale, è inevitabile imbattersi in bug, incidenti e problemi di comunicazione.
Per un perfezionista questa è appunto una grossa frustrazione, così come lo è per il cliente che si aspetta di ottenere qualcosa di funzionante e di farsi capire quando chiede supporto.
La verità, se hai un team che lavora per te, è che devi accettare che questa è la normalità ed è importante impostare correttamente le tue aspettative o quelle dei tuoi utenti, così come degli stessi specialisti che ci lavorano.
Così come il mondo fisico che ci circonda è imperfetto, incompleto e temporaneo, così lo è anche il mondo del digitale.
E’ del tutto evidente a chiunque abbia mai scritto un programma o abbia provato a gestire un progetto di sviluppo, molto probabilmente è capitato anche a te: non importa quanto ci si impegna e quanto si sia circondati da esperti, il software avrà comunque dei bug. Alcuni vengono scoperti, altri no, ma sono comunque esistenti.
L’obiettivo deve dunque essere quello di sviluppare e gestire software e sistemi in modo da minimizzare le imperfezioni, che ci saranno a prescindere.
Innanzitutto, cos’è esattamente un bug e perché si chiama così?
Come prima cosa devi sapere che i bug hanno una lontana origine, precisamente nel settembre 1947: più di 70 anni fa!
Oggigiorno si parla di bug per indicare tutte quelle situazioni in cui un programma si comporta in maniera anomala, non conforme alle intenzioni di chi l’ha scritto, per colpa di un errore nella scrittura del codice sorgente.
Un programma che contiene bug (cioè quasi tutti, con tassi di difettosità variabili) si dice che è bacato.
Bug in italiano significa scarafaggio, ed è proprio uno scarafaggio ciò che nel 1947 è stato trovato all’interno di un computer dell’epoca che mostrava segni di malfunzionamento.
Per la precisione, il primo bug ufficiale dell’informatica era una falena rimasta incastrata tra i circuiti, ed è persino stata conservata per ragioni storiche.
Qui sotto puoi vedere l’immagine del cartellino dove è stato conservato questo primo bug.
![](https://www.alexpagnoni.com/wp-content/uploads/2017/11/first_bug.jpg)
Come uno scarafaggio può fare più o meno ribrezzo e può essere più o meno dannoso, anche i bug del software possono avere diversi livelli di gravità e, a seconda del codice, fare più o meno schifo.
Non si contano infatti le imprecazioni dei programmatori quando si ritrovano davanti errori grossolani.
Sempre per rimanere in tema di definizioni storiche, quando si corregge un bug si dice anche, in alcuni casi, che si applica una “patch”.
Come molti termini informatici che vengono dal passato (come gli stessi bug), non c’è più una correlazione diretta tra il significato letterale e quello che ha assunto in tempi più moderni.
Anche se oggi tra cloud, intelligenza artificiale, macchine virtuali, container, ecc. abbiamo una visione tutta digitale del software, la verità è che una volta il codice veniva scritto manualmente in appositi supporti rigidi chiamati schede perforate applicando, appunto, dei fori.
Oggi correggere un bug significa cambiare una o più righe di codice, eliminando, modificando o aggiungendo codice a piacimento.
Quando c’erano invece le schede perforate, correggere i bug era un processo laborioso e ben più costoso.
E’ per questo motivo che, quando era possibile, per effettuare le correzioni venivano applicate delle pezze (che sono appunto le “patch”) per chiudere i fori sbagliati e aggiungere i fori corretti, proprio come nell’immagine qui sotto.
![](https://www.alexpagnoni.com/wp-content/uploads/2017/11/the_patch.jpg)
Un bug può essere quasi irrilevante, come un errore in un’etichetta, ovvero un testo che ad esempio descrive un elemento nell’interfaccia utente.
In quel caso si dice che l’errore, come spesso la stessa correzione, è “trivial” (è proprio l’etichetta usata in sistemi di bug tracking molto diffusi presso le aziende più strutturate come Jira).
Ci sono però anche casi di bug molto più gravi, che possono produrre il crash dell’applicazione, gravi errori di calcolo, problemi di performance e persino conseguenze nefaste: ci sono stati casi di errori in apparecchiature radiologiche che hanno letteralmente ucciso i pazienti.
Persino l’hardware può essere bacato, come dimostrato parecchi anni fa in alcuni errori grossolani nei processori Pentium e soprattutto in questo ultimo periodo con le gravi vulnerabilità chiamate “Meltdown” e “Spectre”.
Uno degli aspetti più attuali e preoccupanti dei bug in questi ultimi anni, sono infatti i problemi di sicurezza derivanti da vere e proprie vulnerabilità del software causate dai bug.
Con la forte accelerazione sperimentata recentemente nel processo di digital transformation, sempre più grandi quantità di codice sono esposte all’uso pubblico con applicazioni web e altri sistemi sempre più interconnessi (es. Internet of Things).
Contemporaneamente, sono cresciuti esponenzialmente gli attacchi che vanno a sfruttare proprio questo tipo di vulnerabilità presenti nel codice, portando anche all’esposizione di grandi quantità di dati privati, accessi non autorizzati, ramnsonware e altro.
Tutto questo ha portato all’ennesima invenzione di nuovi termini informatici come quello del DevSecOps, che è l’unione tra DevOps e Security, e a nuovi interventi legislativi come quello delle regole del GDPR.
Questo tipo di attacchi è tra le conseguenze più gravi, costose e difficili da rimediare dei bug, che possono causare danni alla reputazione dei brand e conseguenze disastrose di lungo termine per questi ultimi e i loro utenti (come nel caso di Ashley Madison, brand devastato dai danni con tanto di suicidi tra i clienti).
E’ anche per questi motivi che si rende sempre più necessario prevenire il più possibile l’insorgere di bug e altri errori prima che che si verifichino con conseguenze negative, se non catastrofiche.
La qualità deve essere il più possibile insita nel processo di sviluppo in sé, adottando le tecniche di progettazione, le pratiche di sviluppo e i metodi che portano a intercettare buona parte di questi errori durante lo sviluppo stesso più che nelle fasi successive.
Il non fare nulla per migliorare questo processo, non è un’opzione viste le possibili conseguenze.
Pratiche e metodi per assicurare una qualità elevata
Da questo punto di vista, limitandosi allo sviluppo software, ci sono varie pratiche e metodi che assicurano un’elevata qualità e aiutano a mitigare la presenza di anomalie.
E’ necessario tuttavia premettere che per scrivere software solido, bisogna ancor prima pensare a progettarlo come tale. Codice formalmente ben scritto ma dall’architettura poco flessibile o che contiene veri e propri errori progettuali è molto più incline ad essere problematico e difficile da correggere con le tecniche di refactoring.
Test Driven Development
Ad esempio, il Test Driven Development (TDD), dove si scrivono dei test ancor prima di realizzare il codice, dopodiché si scrive il codice in grado di far passare questi test.
Questo aiuta a dimostrare che il codice che stiamo scrivendo faccia quello che ci si aspetta, inoltre serve a intercettare future regressioni (che significa in sostanza tornare indietro in termini di qualità) quando si scrive codice che impatta in quello già esistente.
Come beneficio ulteriore, il fatto che il codice sia scritto in un modo facilmente testabile è un segno che potenzialmente è anche ben architettato e progettato, anche se non è una garanzia.
Per essere testabile, il codice deve essere infatti scritto in un certo modo che “costringe” i programmatori a non prendere una serie di brutte scorciatoie.
Inoltre, dopo aver scoperto e sistemato un bug, andrebbero scritti anche i test per verificare che questi bug non si presentino più.
Il TDD è quindi una tecnica che viene attuata in locale nella macchina usata dai programmatori, che costituisce l’ambiente di sviluppo.
Anche se non è direttamente correlato al tema del TDD, è comunque essenziale disporre di ulteriori ambienti di test, separati sia da quelli di sviluppo che da quello di produzione. Avere quindi degli ambienti di test è il minimo sindacale in qualsiasi serio progetto digitale, come quello per la continuous integration (descritto più avanti) e quello per i test di accettazione utente (UAT, spesso impropriamente definito “staging”).
Pair Programming
Un’altra pratica importante è quella del pair programming, cioè quella di programmare in coppia lavorando sullo stesso codice, alternandosi nella scrittura.
Questo aiuta molto nel far sì che uno sviluppatore si accorga di un problema anche se l’altro programmatore nella coppia non se n’era accorto.
In più, i buchi nell’esperienza di uno dei programmatori sono più facilmente coperti dall’esperienza dell’altro, e viceversa. Anche un programmatore molto esperto ha inevitabilmente delle lacune.
Quello dello sviluppo in coppia è quasi sempre visto dai clienti e da chi gestisce un gruppo di lavoro come un grosso spreco di risorse, praticamente come un raddoppio di costi.
Se questo è ciò che pensi, ti stai sbagliando di grosso. Il pair programming non aumenta i costi, li riduce. Il problema nasce dal fatto di “contare le ore” invece che apprezzare la qualità e i minori problemi che ci saranno (facendoti risparmiare le TUE ore).
Continuous Integration
Un ulteriore metodo per migliorare la qualità del software è quello di sottoporlo all’integrazione continua, in termini tecnici chiamata continuous integration: quando il codice viene inviato nel cosiddetto repository remoto, oppure ad intervalli con un certa frequenza, i test vengono lanciati in automatico in un apposito ambiente senza che debba farlo manualmente qualcuno.
Se ci sono problemi e il software non passa i test, non può neanche essere inviato in produzione.
Le pratiche e i metodi utilizzati possono assicurare la qualità al 100%?
Ma anche impiegando tutte queste pratiche, è del tutto realistico pensare che ci possano comunque essere dei casi mancanti, dei problemi latenti o degli errori di progettazione.
Oppure potrebbe essere sfuggito un requisito del committente, che magari non è neanche mai stato espresso né raccolto correttamente.
O, ancora, solo dopo averlo realizzato, ci si accorge che il sistema ha un comportamento che non è realmente quello desiderato, anche se tecnicamente corretto.
Oltre ad essere imperfetta, una piattaforma digitale soffre anche del problema di essere del tutto temporanea e volatile.
Per capire meglio cosa intendo, possiamo fare un paragone con la versione più antica di questo problema, che chi è più grande conosce sicuramente.
Tanti anni fa, ad esempio, i programmi e i dati venivano memorizzati su floppy disk e hard disk che avevano la tendenza a corrompersi o smagnetizzarsi (e comunque neanche oggi le memorie sono del tutto sicure).
Oggigiorno questo è un problema molto meno sentito, sia perché si usano supporti e file system più affidabili oltre a sistemi ridondati, sia perché molti di questi dati e codici si sono spostati sul cloud (quindi da problema tuo è diventato problema, almeno in teoria ma non sempre nella pratica, di qualcun altro), ma si verifica comunque ad esempio in forma di migrazioni di piattaforme e aggiornamenti che possono fare perdere informazioni.
Ad esempio, l’aggiornamento dei linguaggi o l’utilizzo di piattaforme che ad un certo punto non vengono più supportate rendono più concreto il problema della temporaneità.
Un’azienda che ha realizzato qualche anno fa la propria piattaforma digitale, fino a poco tempo fa perfettamente funzionante, si potrebbe trovare nella condizioni di doverla modificare aggiungendo nuovi componenti o integrarsi a sistemi terzi che richiedono tuttavia la versione aggiornata del framework utilizzato per costruire la piattaforma.
E’ del tutto possibile e frequente che se il framework o il CMS ha qualche anno e non è mai stato aggiornato, non sia più possibile effettuare questo tipo di operazioni senza un grosso e costoso lavoro di migrazione.
Questo è un problema anche in termini di sicurezza: avere codice non aggiornato o non aggiornabile espone ad attacchi di ogni genere, che più passa il tempo e più sono costosi per l’impatto devastante che possono avere visto che si sta spostando quasi tutto sul digitale.
Non si può risolvere il problema della temporaneità, ma lo si può mitigare riducendo la quantità di sistemi terzi integrati, ad esempio componenti, librerie di codice ed API esterne, e utilizzando degli strumenti di package management (Composer in PHP, npm per Javascript, e così via).
Inoltre, se ci si aspetta che la propria piattaforma continui ad essere sicura e sempre pronta per essere migliorata ed estesa, è necessario pianificare del tempo per un’adeguata manutenzione, per aggiornare i pacchetti software che la compongono e per migrarla quando necessario alle nuove versioni del framework o prodotto utilizzato alla base.
Non è qualcosa che puoi pensare di accantonare, perché magari non c’è budget in quel momento o perché ci sono funzionalità più importanti da consegnare.
Quando arriverà il momento, sarà troppo tardi e sarà un bagno di sangue che sistemare ti costerà molto di più che se non fosse stato fatto a tempo debito.
Infine, non accade quasi mai che un prodotto sia realmente concluso e che ogni funzionalità sia stata realizzata.
Questo perché chi ha in mente un prodotto ha tipicamente più idee di quante risorse ne abbia per realizzarlo. E se sei un imprenditore o un manager, sono abbastanza sicuro che questo descriva anche la tua situazione.
O anche perché una volta che un prodotto arriva nelle mani degli utenti, molte delle assunzioni iniziali che hanno portato a realizzarlo in un certo modo si rivelano non adeguate se non errate ed è necessario adattarlo, considerando anche le mutevoli condizioni del mercato e i prodotti della concorrenza che evolvono a loro volta.
Questo, peraltro, è in linea con quanto affermo normalmente sulla necessità di non ragionare a progetto quando si realizza una nuova piattaforma o servizio digitale, perché quasi mai ha realmente una fine nel breve/medio termine.
In sostanza, sebbene sia più facile focalizzarsi sui problemi che si pongono quando si sviluppa un prodotto digitale, accettare il fatto che quest’ultimo non sarà mai perfetto, permanente e completo, né che potrà stare in piedi da solo, è la chiave per non creare aspettative irrealizzabili e frustrazione, focalizzandosi invece sul modo per mitigare questi problemi.
Chi deve pagare per il bug fixing e la risoluzione dei problemi?
Se quindi la presenza di bug, il verificarsi di incidenti e inefficienze nell’operatività quotidiana sono inevitabili in fase di sviluppo, esercizio e manutenzione di un prodotto digitale, d’altra parte si pone una domanda fondamentale: chi deve pagare questi problemi?
Sotto questo aspetto, non è raro che i clienti (interni o esterni) pensino che a dover pagare per i bug e i problemi di questo genere debba essere il team di sviluppo.
E’ un ragionamento naturale e del tutto comprensibile. Ma molto pericoloso e, in realtà, controproducente per il cliente.
Pensare solamente di prendersela con il team di sviluppo, magari sanzionandolo in qualche modo, non è affatto l’approccio corretto, per i motivi che indicherò più avanti.
Potrebbe sembrarlo nel breve termine, ma non lo è nel medio/lungo termine: potrebbe anzi essere un disastro finanziario.
Prima di spiegare i motivi, è necessario saltare alle conclusioni e anticipare la risposta corretta e definitiva alla domanda “chi paga i bug”: è il cliente a dover pagare i bug e i problemi, ma a sua volta il team deve imparare da questi ultimi. E’ essenziale che le due parti vadano di pari passo.
Mi rendo perfettamente conto che questa risposta può stupire e rendere confusi, in particolare se detta da me.
Chi mi conosce e usufruisce dei servizi miei e della mia azienda, sa perfettamente che Innoteam offre una serie di garanzie uniche ed è orientata al risultato finale al punto che gestiamo le attività per conto dei clienti con ruoli di responsabilità. Quindi perché i clienti dovrebbero pagare questi errori?
Cosa stai pagando realmente?
La confusione e lo stupore nascono dal fatto che quasi sempre c’è un equivoco di fondo su cosa il cliente sta pagando effettivamente.
E’ infatti facile rimanere intrappolati nella mentalità che un rilascio o una consegna di uno sviluppo di un fornitore o di un team di sviluppo debbano essere corredati da una garanzia che copre eventuali bug.
E’ del tutto normale che un cliente abbia l’aspettativa di ottenere ciò per cui sta pagando, è ciò che mi aspetto anche io stesso.
Il punto è che confusione proprio su questo aspetto: è l’aspettativa ad essere errata, non ciò che si sta ottenendo.
Per farti capire cosa intendo, è importante comprendere che lo sviluppo di software su misura non deve essere confuso con l’acquisto di un’applicazione standard già pronta o l’abbonamento ad un servizio online.
In questi ultimi casi è giusto aspettarsi un’esperienza sostanzialmente priva di bug già inclusa nel prezzo, o comunque solo con problemi minori.
Il prezzo comprende infatti già il margine del produttore e i costi sostenuti da quest’ultimo per lo sviluppo, il test, la correzione delle anomalie, oltre a una fetta di costi futuri di ricerca e sviluppo per l’evoluzione del software e la sua manutenzione/correzione di problemi.
Gli eventuali bug devono quindi essere risolti con i futuri aggiornamenti distribuiti dalla software house (o applicati direttamente se si tratta di un’applicazione online in modalità SaaS).
E’ a questo che serve la garanzia nei prodotti software standard.
Quando invece si lavora sulla progettazione e sullo sviluppo di un software e un’infrastruttura su misura, come avviene normalmente in un prodotto digitale complesso, si sta pagando qualcosa di ben diverso, soprattutto se è la primissima versione del prodotto o una nuova caratteristica importante in un prodotto già esistente.
In questi casi, l’azienda che commissiona lo sviluppo e la manutenzione corre una serie di rischi molto importanti.
Ad esempio, quello di costruire il prodotto giusto, finanziarlo, pianificarlo correttamente, integrarsi con l’esterno e impiegare approcci e tecnologie ancora non consolidati se non addirittura ancora da dimostrare. Tutti questi rischi si presentano praticamente in ogni prodotto o servizio digitale degno di nota.
Il motivo vero, quindi, per il quale si paga un team tecnologico, è quello di mitigare questi rischi, non quello di renderlo finanziariamente responsabile se qualcosa va storto in uno di quei punti.
Da questo punto di vista non bisogna fare confusione tra rischio di impresa, che è perlopiù a carico del committente, e rischio tecnologico, che è invece perlopiù a carico del team di sviluppo.
E’ per questi motivi che un’iterazione di sviluppo o un rilascio non corrispondono automaticamente alla garanzia di ottenere un prodotto finito e senza anomalie.
In questo contesto, il team deve dare il massimo nel costruire un prodotto di qualità, e quest’ultima si ottiene nel lungo termine grazie a ciò che si scopre dai prototipi, dai rilasci in beta e dai veri e propri rilasci pubblici in produzione.
I bug vanno trattati come un risultato inevitabile che si scopre in un ciclo più lungo nel processo di sviluppo rispetto a quello delle singole iterazioni di lavoro.
Quando non devi pagare per i bug?
Come scrivevo prima, però, è importante che il team sia focalizzato sulla qualità e che faccia tesoro dei problemi affrontati e dei bug risolti.
Solo in questo caso dovresti essere disposto a pagare i bug di tasca tua.
Per capire se è questo il caso, devi assicurarti che il team usi le tecniche e le pratiche corrette, che sono quelle che ho elencate precedentemente.
Mi riferisco, quindi, alla creazione dei test automatici con un’ampia copertura del codice scritto, all’utilizzo della continuous integration, al pair programming e così via.
Solo se il tuo prodotto è un colabrodo e il tuo team non segue queste pratiche orientate alla qualità dovresti rifiutarti di pagare i bug, e quindi sbarazzarti del team stesso.
Altrimenti, se il team sta lavorando in buona fede e ha adottato queste pratiche, devi considerare i bug come una parte essenziale dello sviluppo che si verifica a valle del lavoro iniziale.
Come devi lavorare con il tuo team?
E’ quindi essenziale disporre di un team di cui ti puoi fidare e adottare una mentalità aperta, comprendendo che non si può ottenere una garanzia implicita quando c’è un rilascio.
Altrimenti succede quello che mi è capitato più volte di vedere in prima persona, portando tra le altre cose a quella che si chiama “lenta marcia della morte“.
Mi riferisco a quei casi in cui ad un fornitore si chiede di pagare di tasca propria i bug, rimanendo peraltro nei tempi concordati, o ai propri dipendenti di fare gli straordinari magari gratis per sistemare i problemi, sempre rimanendo nei tempi pianificati.
Per esperienza posso garantire che in entrambi i casi ciò che si ottiene punendo il team (perché queste sono una forma di punizione e non semplici richieste) è il contrario di ciò che si pensa e non si fa altro che creare disaccordo.
Sempre rimanendo nella premessa che si sta avendo a che fare con un team credibile e non con degli incompetenti, si può dare per scontato che gli sviluppatori stiano usando le best practice e il proprio giudizio professionale per bilanciare la qualità con le esigenze delle consegne.
Quello che succede quando si penalizzano gli sviluppatori quando ci sono dei bug è che questo giudizio professionale viene compromesso perché in reazione poi si dà troppa importanza alla qualità.
Il risultato è che lo sviluppo procede in modo più lento, perché il team avrà paura delle conseguenze di altri errori ed esagererà con i test automatici andando ben oltre al proprio senso dei rendimenti marginali, cioè oltre al punto per il quale tale sforzo non vale la pena, risultando in costi eccessivi rispetto ai risultati.
Ho visto persone terrorizzate che dedicavano ore di analisi eccessivamente approfondite per evitare tirate di orecchie, con il morale a pezzi e un rendimento al minimo.
Un altro effetto collaterale è quello di incentivare di fatto il team a non cercare i bug in fase di sviluppo per non doverne dare evidenza, portando a problemi successivi ancora più gravi quando il software è in produzione e correggere le anomalie diventa molto più costoso.
Conclusione
In conclusione, per ottenere un prodotto di qualità bisogna accettare che ci saranno dei bug e che bisogna incentivare gli sviluppatori a trovarli e a metterli nel backlog, che è la scaletta di lavoro ordinata per priorità.
I bug vanno quindi trattati come gli altri task, vanno stimati quando è opportuno e vanno messi nel backlog come se fossero delle normali funzionalità da sviluppare ai quali dedicare tempo e risorse.
Per fare questo è importante lavorare con un team di persone capaci e finanziarlo per lasciargli dare il massimo nel costruire il tuo prodotto digitale.
In particolare, lavorando con team esterni bisogna assicurarsi di lavorare con i fornitori giusti che siano in grado di fornire opportune garanzie.