Tutto comincia con…
…un bambino di otto anni che vede quello che aveva fatto lo zio di un suo amico: in una notte, utilizzando il linguaggio BASIC, questa persona aveva sviluppato un programma sul Commodore 64 che faceva alzare la bandiera italiana. Era il 1982, l’anno della vittoria italiana nel mondiale di calcio in Spagna, e quella bandiera sullo schermo lo affascinò non poco: come era possibile disegnare sul monitor una bandiera in movimento scrivendo testi con la tastiera?
Come avrete capito, quel bambino ero io, che ovviamente allora non comprendevo concetti come variabili, cicli e cose del genere. Per quanto personale, penso che questa storia sia piuttosto comune, almeno nei suoi tratti essenziali: ogni sviluppatore potrebbe probabilmente raccontare qualche episodio sulla sua storia di bambino nerd; racconti diversi, certamente, ma con la condivisa fascinazione per la tecnologia intesa anche come possibilità creativa.
In questa serie di articoli descriverò la mia personale visione di come il mondo dello sviluppo software stia evolvendo e delle direzioni che sta prendendo: quelle che esporrò non sono indiscutibili verità di fede, ma vanno piuttosto intese come opinioni basate sulle esperienze che ho vissuto e sto vivendo sui progetti, alla luce di una serie di riflessioni e considerazioni.
La “trafila” dei linguaggi
Ogni sviluppatore ha il suo “percorso formativo” personale, che cambia anche a seconda della generazione cui si appartiene: chi ha adesso circa cinquanta anni avrà sicuramente fatto esperienze diverse dai ragazzi che cominciano a frequentare l’università adesso, però a tutti è capitata una sorta di “trafila”: si impara un linguaggio, se ne scopre un altro, ci si perfeziona in qualcosa di ulteriore e così via.
Nel mio caso, imparai a conoscere concetti come tipi, strutture, classi e puntatori alle scuole superiori grazie al linguaggio Turbo Pascal [1]. Poi conobbi il C++ [2]. Mi ricordo ancora il professore — che sviluppava in Clipper [3] — che ci fece vedere i quattro dischetti della versione 2.0 di C++dicendo che era una rivoluzione. All’università conobbi Java e poi C#, linguaggi che stavano cambiando il mondo. E poi, cominciando a fare il “mestiere” di sviluppatore, tante altre esperienze, un po’ per seguire l’evoluzione dei linguaggi, un po’ per sperimentare nuove soluzioni.
Oggigiorno fa impressione vedere come i bambini imparino giocando i concetti di istruzione e variabile — addirittura in età prescolare — grazie a iniziative come code.org [4]. I nostri erano proprio altri tempi: quanto vorrei essere bambino oggi!
Problemi e semplicità
Una costante che accomuna tutte le generazioni di sviluppatori, indipendentemente dalla loro formazione specifica, è probabilmente quella di aver visto tutta una serie di soluzioni volte a semplificare una serie di problemi simili e ricorrenti. Almeno una volta nel corso della nostra carriera, tutti ci siamo dovuti confrontare con problemi relativi alle seguenti tematiche:
- gestione dei files;
- persistenza e ricerca dei dati su database;
- UI layout capricciosi;
- date e orari che sballano in formattazione, timezones errate e cambio tra ora solare e ora legale;
- riuso del codice come buon proposito iniziale… che rimane alla fine purtroppo solo un buon proposito;
- indecisione su quali frameworks e librerie usare;
- portabilità delle applicazioni su diversi sistemi: scrivo una volta e “gira” su tutto.
Questi sono solo alcuni problemi che, come programmatori, stiamo affrontando ogni giorno. Essendo problemi ricorrenti sono emersi pattern per risolverli che però non sempre semplificano e snelliscono il software risultante. Quindi i problemi permangono. Come mai?
Sistemi complessi
Le applicazioni software tendono ad essere sistemi complessi per i quali non è possibile garantire che funzionino in tutti i casi possibili. Molti passi in avanti sono stati fatti nell’automazione dei test e nel controllo statico e dinamico della qualità del software.
Ad esempio xUnit [5] — di cui jUnit in ambito Java forse è l’esempio più conosciuto — aiuta a garantire che i bug di regression non siano così frequenti. Altro ottimo strumento è JSLint [6] che analizza staticamente il codice per scovare cattive abitudini di scrittura che tendono a creare un codice “spaghettoso” — dopo “petaloso”, vale tutto — e difficile da mantenere. E accanto a questi due esempi, esistono molti altri strumenti che aiutano a migliorare la qualità del codice.
L’errore però rimane comunque dietro l’angolo. Come mai?
Perché chi progetta e programma sono persone e le persone sbagliano. Sbagliano non tanto nel codificare quanto nel tradurre le logiche del servizio reale in un linguaggio che sia comprensibile ad una macchina.
Strumenti per la semplificazione
Esiste una soluzione a questi problemi? Una soluzione unica non esiste. Esistono standard per la programmazione, framework che aiutano ad incasellare e risolvere le problematiche simili con un approccio che è ripetibile e che funziona il più delle volte. Nascono ed evolvono linguaggi di programmazione sempre più semplici ed efficaci. Agile con eXtreme Programming, Scrum, Kanban e altri concetti aiuta le persone a organizzare meglio il codice, le idee e il lavoro. E si potrebbe andare avanti
Tutte queste risorse ci aiutano a migliorare la qualità e a dominare la complessità. Ma più questi “strumenti” sono disponibili e accessibili più il mondo reale diventa complesso e di conseguenza diventa complessa la sua formalizzazione in applicativi software.
Più la tecnologia evolve, più la complessità del mondo reale aumenta e più tecnologie nuove sono necessarie. È una regola che vale per diverse tipologie di industria: quella del software è una di quelle in cui questa curva è sempre più verticale. E quindi, ritorna la domanda iniziale: “Esiste una soluzione?”.
Dominare la complessità
Le informazioni di giorno in giorno sono sempre di più complesse. Miliardi di relazioni, petabyte di dati, business astronomici che orbitano intorno a questi dati. Un errore può portare a perdite importanti. Come dominare questa complessità? Per quanto possa sembrare banale, la prima regola di buon senso sta nel cercare di non introdurre ulteriore complessità nello sviluppo software.
Dico spesso ai miei colleghi e clienti: “I tecnici si innamorano della tecnologia”. È frequente vedere sistemi complessi che risolvono problemi non altrettanto complessi: e questo avviene proprio perché si decide di usare una tecnologia complessa per affrontare situazioni che forse non la richiederebbero. Questa complessità non necessaria porta a creare sistemi con branch di codice non necessario e quindi con possibili errori dietro l’angolo.
Dalle logiche applicative ai linguaggi di programmazione
Il problema che abbiamo individuato sopra è quello della “traduzione”: come tradurre logiche, concetti, necessità del mondo reale in un linguaggio che sia comprensibile da una macchina? Questo scoglio da superare non è tanto un problema filosofico, ma è il nucleo della questione dello sviluppo software.
Riuscire a esprimere in modo naturale le logiche di business è una sfida non indifferente. Sono nate centinaia e centinaia di linguaggi di programmazione: basta andare su Wikipedia per trovarne un elenco non esaustivo [7].
I linguaggi sono nati per dominare questa complessità; e, se non bastasse, sopra i linguaggi sono nate librerie e framework, e sono nati vari server quali database server, application server e così via. Insomma, un mondo vastissimo dove orientarsi diventa difficile tanto che i trend si ripetono ciclicamente, come sarà capitato di notare a chi è nel mondo del software da più di qualche anno.
Influenze e “contaminazioni”
Guardiamo un po’ a quello che è successo nella musica dal secondo dopoguerra in poi: un tempo c’erano i “generi musicali”, ben distinti, ma da un certo punto in poi caratteristiche delle diverse “specialità” si sono mescolate influenzandosi: rock e funky, elettronica e soul, reggae e musica neomelodica (sì… anche questo).
Un processo analogo è accaduto nei linguaggi di programmazione: certe rigide suddivisioni in cui eravamo abituati a catalogare i nostri ferri del mestiere (linguaggi interpretati, linguaggi tipizzati, linguaggi dinamici) con il passare del tempo hanno visto i loro confini sempre più sfumati. Gli approcci concettuali di un linguaggio e le strutture di un altro si influenzano tra loro. È quello che è accaduto con caratteristiche dei linguaggi tipizzati che si ritrovano nei linguaggi dinamici — ad esempio con i TypedArrays in ES6 [8] — oppure con le influenze dei linguaggi dinamici in quelli tipizzati — come i var in .NET [9] — o con quello che sarebbe possibile vedere in altri numerosi esempi.
Tutto questo perché? Perchè milioni di programmatori ogni giorno risolvono problemi, cercando modi sempre più semplici. È per questo che assistiamo a trend e migrazioni tra linguaggi: per cercare la semplicità nella programmazione.
Nonostante questa tendenza alla semplificazione, non ci si devono fare facili illusioni: al momento non esiste un’unica scelta come si evince dall’analisi dei più famosi siti [10]: se JavaScript è diventato lo standard per il frontend, per il backend c’è di tutto…
Analizzare le tendenze
Stackoverflow ha stilato una classifica dei linguaggi più usati nel 2015 con il survey pubblicato ad inizio 2016 [11].
Il linguaggio più usato risulta JavaScript grazie sopratutto al suo dominio sul frontend e, almeno nel breve periodo, non si vedono all’orizzonte grandi rivoluzioni. Ma allora, vista la dinamica e il motivo dell’evoluzione, ha ancora senso ricercare il linguaggio ideale, il framework definitivo, l’architettura stabile? Probabilmente no, ed è quello che vedremo nel corso di questa serie. I “superpoteri” risolutivi che ancora venti o dieci anni fa venivano attribuiti a ogni nuovo linguaggio, o a ogni nuova versione di linguaggi che si rinnovavano, ci appaiono oggi entusiasmi eccessivi.
Cosa scegliere, quindi?
Ma questo non ferma la ricerca, anzi! Ha senso infatti trovare delle soluzioni per approcciare i problemi con semplicità e pragmatismo favorendo realizzazioni più lineari anche a discapito della perfetta ingegnerizzazione. A mio avviso è preferibile un sistema di cui riesco a dominare la complessità e che posso modificare senza perdere il controllo, rispetto a un sistema che fa moltissime cose, delle quali però… me ne serve solo una piccola parte.
Per questo scegliere un linguaggio per tutto a mio avviso non ha molto senso. Ha senso sceglierlo in base a cosa si deve fare e mettersi nelle condizioni di poter usare altri linguaggi e diverse tecnologie se necessario. Martin Flower spiega nel dettaglio nel suo articolo del 2014 cosa sono i microservices [12].
Stiamo entrando nell’era delle architetture poliglotte dove la complessità si sposta dall’organizzazione dei framework alla coreografia dei servizi. Un mondo di programmazione dove non c’è più il concetto di giusto o sbagliato, di stabile o dinamico ma in cui il “comandamento” principale diventa: “evitare di creare codice che non serve”. Un modo di riduzione delle linee di codice e di attenzione all’ecosistema dell’architettura.
Conclusioni
Con questa panoramica ad alto livello di astrazione sull’evoluzione dei linguaggi abbiamo messo in luce come, oggi più che mai, sia necessario puntare non tanto a trovare “il” linguaggio universale, quanto a programmare riducendo al minimo il codice non strettamente necessario.
Nel prossimo articolo parleremo di architetture e di come queste stanno evolvendo — o meglio devolvendo — in sistemi totalmente distribuiti, disaccoppiati e scalabili come in natura sono le formiche, le api o i banchi di pesci.
Tutto questo per riuscire a dominare la complessità di un mondo che sta diventando sempre più difficile da comprendere.