Una tendenza che appare ormai evidente da vari anni (forse da un paio di decenni) nel settore ICT è quella di una sempre maggiore complessità che non risulta giustificata dalle esigenze, e che definirei anzi in aperta contraddizione con le dichiarazioni di chi opera nel settore e con il sentire comune dell’opinione pubblica. Tre sono gli aspetti da considerare:
- Lo sviluppo e la manutenzione del software;
- La sua efficienza;
- La sua sicurezza.
Naturalmente efficienza e sicurezza sono qualità molto generali e forse non basterebbe neanche un articolo intero solo per definirle in maniera precisa. Userò quindi delle definizioni molto pratiche, direi operative.
- Con efficienza intenderemo cercare di minimizzare, per quanto possibile contemporaneamente, il tempo di esecuzione ed il costo (inteso soprattutto come consumo energetico) dell’hardware utilizzato.
- Con sicurezza intenderemo minimizzare la superficie di attacco senza impattare sulle funzionalità e la usabilità.
Un approccio leggero allo sviluppo del software
Sviluppare (e manutenere) il software non è mai stato semplice. L’adagio “era meglio prima” non si applica.
Nel passato il processo di sviluppo del software era molto più difficoltoso perché c’erano pochi strumenti, i tempi di compilazione erano lunghi, e la qualità stessa dei compilatori lasciava molte volte a desiderare. Nonostante ciò, sia in ambito commerciale, sia in ambito accademico o comunque non di diretto utilizzo commerciale, sono stati risolti moltissimi problemi con software la cui complessità rimaneva sotto controllo. Anche la parola complessità andrebbe però meglio definita. In questo caso vorrei usare una citazione letteraria, definendo la complessità come l’opposto della leggerezza descritta nelle prima delle Lezioni Americane di Italo Calvino che precisa “La leggerezza per me si associa con la precisione e la determinazione”. Un esempio classico di approccio leggero allo sviluppo del software è quello che avevano seguito Dennis Ritchie e Ken Thompson nell’implementazione di quello che poi sarebbe diventato UNIX ed il suo ecosistema di comandi e strumenti. Vari autori (oltre a Ritchie e Thompson) hanno cercato di sintetizzare quella filosofia in un insieme di vere e proprie regole. Senza entrare troppo nel dettaglio ne ricordiamo due:
- Scrivere programmi che fanno una cosa e la fanno bene (con tanto di acronimo: DOTADIW, “Do One Thing And Do It Well).
- Scrivere programmi che funzionino insieme.
Il meccanismo fondamentale per il funzionamento congiunto è la pipeline. L’output di un programma diventa l’input di quello successivo in una catena che conduce al risultato desiderato.
Un esempio pratico
Per far capire la potenza di questo approccio leggero uso spesso nei corsi il seguente esempio. Supponiamo di avere un testo di grandi dimensioni (centinaia o migliaia di “pagine”) e di dover ordinare le parole che compaiono nel testo in ordine decrescente, da quella che compare più volte a quella che compare meno volte, con a fianco il numero di volte in cui è presente. Quale software mi serve per risolvere questo problema? Problema che, tra l’altro, nell’epoca dell’analisi di report, testi raccolti in rete, file di log e così via è meno artificiale di quello che si potrebbe pensare. La risposta è che non devo scrivere praticamente neanche una riga di software perché è sufficiente combinare programmi esistenti come segue
tr -s ‘ ‘ ‘n’ < Testo | sort | uniq -c | sort -n –r
Per chi ha familiarità con l’ambiente Unix dovrebbe essere ragionevolmente chiaro, ma chiunque può capirlo con una breve spiegazione. Ci sono 4 passaggi
- tr -s ‘ ‘ ‘n’ < Documento legge il testo (supponiamo sia in un file che si chiama Documento) e sostituisce (comando tr che sta per translate) tutti gli spazi con “a capo”. In pratica mette tutto il testo in una colonna (una parola per riga)
- sort ordina alfabeticamente le parole. La conseguenza dell’ordinamento è che le parole uguali si troveranno ripetute una di seguito all’altra
- uniq –c elimina i duplicati ma mette vicino alla parola il numero di volte che compariva prima dell’eliminazione. Se la parola casa compariva 10 volte di seguito dopo l’ordinamento, comparirà adesso una volta preceduta da 10
- sort -n –r di nuovo ordina, ma questa volta numericamente ed in ordine decrescente
Ci sarebbero dei dettagli da considerare (tipo la punteggiatura) ma in sostanza il problema è risolto. I programmi tr, sort e uniq non è necessario scriverli. Sono efficienti nella stragrande maggioranza dei casi e sono sicuri, se non altro perché testati in decenni di utilizzo (ovvio che questo non è una garanzia ma diciamo che aiuta). Si potrebbe obiettare che sono cose da nerd e che funzionano solo sotto Unix, ma in realtà una funzionalità simile a quella di uniq e tr si può ottenere con la PowerShell in Windows (sort è già disponibile). L’obiezione che sono cose da nerd è più rilevante perché le società che sviluppano software o fanno consulenza dovrebbero in effetti avere almeno una componente nerd. Invece la stragrande maggioranza delle società del settore in Italia (soprattutto quelle grandi) considera questo tipo di approccio poco professionale. Non è semplice stabilire se la scelta è dettata dalla mancanza di conoscenza o da una precisa volontà. Invece la conseguenza della scelta è chiara. Viene sviluppata e venduta un’enorme quantità di software di scarso valore.
Perché l’efficienza del software è così sottovalutata
L’inefficienza viene giustificata con la mancanza di adeguate risorse hardware. Ma la triste verità è che l’efficienza del software è raramente considerata un requisito in fase di disegno ed implementazione. Il motivo può essere che nella maggior parte dei casi neanche viene in mente che il software può essere più o meno efficiente. Per molti tra coloro che sono chiamati a fare scelte di una certa importanza, le prestazioni sono legate solo alla potenza dell’hardware (locale o in cloud non fa differenza). Se si guarda solo alla capacità di elaborazione dei moderni sistemi, non si spiega perché molte applicazioni non reggono il carico di lavoro e vanno, come si dice un po’ semplicisticamente, in tilt.
Se si considera invece, con un occhio critico, il software con cui sono scritte quelle applicazioni, allora si capisce come sia possibile che per calcolare la media e la varianza di un milione di numeri ci possano volere anche svariati secondi. Fino a qualche tempo fa, quando i tempi di risposta non erano più accettabili si faceva un aggiornamento dell’hardware che ovviamente era ben visto non solo dal fornitore dell’hardware ma anche dagli sviluppatori del software perché il nuovo hardware magari richiedeva una nuova versione del sistema operativo che a sua volta implicava l’aggiornamento dei vari strati di middleware, tutto pensato per lasciare l’applicazione in sostanza come era prima, cioè lenta perché scritta senza avere l’efficienza tra i requisiti.
Complesso non vuol dire efficiente
Con l’attuale e sacrosanta attenzione ai consumi energetici questa perversa spirale si spera possa essere interrotta. Serve però un approccio leggero che permetta l’efficienza. Il software complesso ben difficilmente è efficiente. A scanso di equivoci, non sto facendo riferimento all’agile programming che propone un modello iterativo di sviluppo ma non presta specifica attenzione all’aspetto dell’efficienza. Altro equivoco da evitare, non esiste un linguaggio di programmazione che magicamente garantisce l’efficienza. Un’applicazione può essere scritta in C (o C++) ed essere comunque lenta come un’agonia.
Python o altri linguaggi di scripting possono essere usati per sviluppare applicazioni efficienti sfruttando la possibilità di integrare librerie scritte, tipicamente, in C o C++. È anche vero che questo, in qualche modo, contraddice il principio della leggerezza: è proprio necessario usare un linguaggio di scripting per invocare una libreria scritta in C++? Siamo sicuri non sia più semplice chiamare direttamente la libreria da un leggero programma compilato?
Leggerezza e sicurezza
La leggerezza, e l’essenzialità sono cruciali anche per aumentare la sicurezza del software. Tutti parlano dell’importanza della sicurezza: security-by-design è lo slogan del momento ma purtroppo viene spesso confuso (anche qui non è facile capire quanto involontariamente) con il security-by-obscurity dato che si riduce spesso all’aggiunta di componenti black box “per la sicurezza” che non modificano l’applicazione. In queste situazioni viene subito in mente l’aforisma di Henry Ford “quello che non c’è non si può rompere”, che potrebbe essere adattato in “quello che non c’è non può avere un problema di sicurezza”. Non è certo che Ford abbia detto veramente quella frase ma è certo che un’applicazione debole dal punto di vista della sicurezza non diventa più sicura ma solo più complessa, meno leggera, se viene semplicemente “infilata” in un framework, in un ambiente di virtualizzazione o gli viene aggiunto un componente alieno.
Il software rimane, insieme al fattore umano, l’elemento più critico per la sicurezza. Se si vuole veramente migliorare la situazione, non si può continuare ad aggiungere strati su strati di software assolutamente inutile dal punto di vista funzionale che, a differenza di quanto sbandierato da chi lo propone, assorbe risorse ed amplifica la superficie di attacco per i malintenzionati.
Conclusioni
Per onestà bisogna riconoscere che la sindrome da complessità sta mietendo vittime anche nell’ambito del software open-source. L’installazione di molti pacchetti open-source è diventata una sorta di lotteria legata al soddisfacimento di una serie infinita di prerequisiti di cui spesso non si capisce la necessità. Alla fine, spesso si è costretti a ricorrere alla creazione di macchine virtuali dove installare da zero tutto quello che serve oppure ai container che girano in ambienti come Docker. La virtualizzazione è utile in vari casi (tra l’altro non è certo un’invenzione recente, il VM dell’IBM festeggia il prossimo anno i suoi 50 anni) ma come tutte le tecnologie andrebbe usata quando effettivamente serve, non per nascondere sotto il tappeto le inconsistenze di sistemi software sempre più, inutilmente, complessi e quindi difficili da tenere sotto controllo.
Concludo con una citazione di Tony Hoare. Il nome probabilmente non dice molto, ma è la persona che ha inventato uno degli algoritmi più famosi al mondo: il quicksort. Nel discorso di accettazione del Premio Turing 1980 (più di 40 anni fa), tra l’altro disse: “There are two ways of constructing a software design: one way is to make it so simple that there are obviously no deficiencies and the other way is to make it so complicated that there are no obvious deficiencies”.
Parole che andrebbero sempre tenute a mente quando si parla di software.