#Blog

I miei primi 10 anni da sviluppatore

Una riflessione sui miei primi 10 anni nel mondo dello sviluppo, dalle prime righe di codice ad oggi.

30/06/2024

Per garantire una maggiore leggibilità, i post del blog rispecchiano il font di default impostato dal sistema, anziché il comune Press2Start presente nel resto del sito.

🇬🇧 Are you looking for the english version? Click here

Dieci anni.

Alcuni giorni fa ho compiuto 30 anni. E ho realizzato che lavoro nel settore dello sviluppo software da dieci anni. Dieci anni di codice, bug, pull request e rilasci in produzione. Dieci anni di apprendimento, fallimento, successo e crescita. Dieci anni di sfide, opportunità ed esperienze che mi hanno trasformato nello sviluppatore che sono oggi.

Il nostro settore è in continua evoluzione e gli ultimi dieci anni non hanno fatto eccezione. Ho visto le tecnologie andare e venire, i trend salire e scendere e i paradigmi cambiare e adattarsi.

Oggi voglio condividere alcune delle lezioni che ho imparato lungo il percorso, le intuizioni che ho acquisito e i principi secondo i quali ho imparato a vivere. Queste non sono solo lezioni tecniche ma anche personali e professionali che mi hanno aiutato a crescere ed evolvermi come sviluppatore e come persona.

P.S. Ho riscritto (o dovrei dire rifattorizzato?) questo stesso post almeno 10 volte, cercando di essere il più conciso possibile, senza perdere l’essenza di ciò che volevo trasmettere. Spero di esserci riuscito. Nel caso in cui non l’avessi fatto, che sia un promemoria che la perfezione è un viaggio, non una destinazione.

Un po’ di storia.

Ho iniziato la mia carriera in Irlanda come sviluppatore in erba per una piccola agenzia, lavorando su siti web WordPress e PHP. Mi sono poi trasferito in Italia per una grande azienda multinazionale, dove ho lavorato principalmente come sviluppatore front-end con un piccolo salto in Python-landia, per poi passare al ruolo di sviluppatore full-stack in una piccola web agency locale, agendo anche come Project Manager per piccoli clienti. Successivamente sono entrato in una società di consulenza con 20 sviluppatori fungendo sia da sviluppatore che da team leader/project manager, mettendomi alla prova con i miei primi clienti internazionali. Mi sono poi trasferito in una piccola startup, dove sono stato l’unico sviluppatore per la maggior parte del tempo, e ho dovuto costruire da zero l’intero flusso di lavoro digitale. Alla fine, sono approdato in un’azienda di prodotti di medie dimensioni, dove ora sono responsabile di un team di 6 sviluppatori, in qualità di Tech Lead e Software Architect.

Molti cappelli indossati, esponenzialmente più errori commessi e di conseguenza molte lezioni apprese. Ho avuto l’incredibile fortuna di trovarmi in contesti diversi, con sfide diverse e persone diverse, e sono grato per ognuna di queste cose. Inoltre, ho avuto la fortuna di entrare in contatto e lavorare con alcune delle menti più brillanti del settore, e non potrò mai ringraziarle abbastanza per le lezioni che ho appreso da loro.

Cosa è cambiato nel settore?

Praticamente tutto. Quando ho iniziato, jQuery era il re indiscusso, WordPress era la soluzione standard per ogni sito web e i prefissi -moz- e -webkit- erano ancora in voga. Oggi, possiamo costruire intere applicazioni con un singolo comando, abbiamo strumenti che possono scrivere codice per noi, e possiamo distribuire le nostre applicazioni in tutto il mondo in pochi secondi. L’industria si è evoluta a un ritmo incredibile, ed è stato sia emozionante che impegnativo tenere il passo con tutto questo.

Eppure, sembra che in fondo nulla sia cambiato: abbiamo ancora basi di codice legacy, test instabili e 42 modi per centrare un div. Inoltre, non sappiamo ancora come fare stime.

Cosa è cambiato in me?

Ho iniziato come sviluppatore la cui unica preoccupazione era scrivere il miglior codice possibile. Ero ossessionato dallo scrivere il codice più performante, pulito ed elegante possibile. Ero convinto che se avessi scritto il miglior codice, tutto il resto sarebbe andato a posto. Che ingenuo che ero.

Negli anni, ho capito che scrivere codice è solo un mezzo per raggiungere uno scopo. Ho iniziato a concentrarmi di più sulle persone, sulla comunicazione e sulla collaborazione, e meno sul codice stesso. Ho imparato che il miglior codice del mondo non vale nulla se non si riesce a lavorare efficacemente con il proprio team, se non si riesce a comunicare le proprie idee, se non si riesce a collaborare con gli altri. Inoltre, business non è solo una parola che usiamo per identificare le persone che ci pagano, ma è il motivo per cui scriviamo codice in primo luogo.

I miei 13 principi.

10 anni, 13 principi. Ho provato a condensarli in 10, ma ho fallito. Spero che mi perdonerete per questo.

0. Il miglior codice è il codice che non hai mai scritto.

Qualche giorno fa ho letto un articolo che parlava della Senior Engineer Fatigue, e mi ha colpito molto. L’articolo afferma chiaramente:

Il miglior codice è il codice che non hai mai scritto.

E non potrei essere più d’accordo. Il miglior codice è il codice che non hai mai scritto, la funzionalità che non hai mai implementato, la libreria che non hai mai costruito. Chiediti e chiedi alle persone intorno a te se hai davvero bisogno di quella funzionalità, di quella libreria, di quel codice. La maggior parte delle volte, la risposta sarà no.

Quindi dovresti smettere di scrivere codice? Assolutamente no. Significa che dovresti essere più attento al codice che scrivi, alle funzionalità che implementi e alle librerie che costruisci. Significa che dovresti concentrarti sulla risoluzione del problema in questione, e non solo sulla scrittura di codice performante e pulito. Significa che dovresti essere pragmatico, non dogmatico.

1. Le persone prima dei processi.

Scambierei una coverage del 100% in un codice da 500k righe per un team che sa comunicare e collaborare efficacemente.

Avere un pipeline CI/CD perfetta, un codice ben documentato, un robusto suite di test e una moltitudine di Senior Developer che lavorano con te è fantastico, ma non vale nulla se Sviluppatore #17 non diventa John o Jane per te.

Come Architetti e Sviluppatori Software, ci concentriamo spesso sugli aspetti tecnici del nostro lavoro come coesione e accoppiamento, dimenticando che quei termini si applicano anche - e lasciatemi dire ad alta voce, principalmente - alle relazioni che costruiamo con i nostri colleghi.

Vuol dire che puoi trascurare l’aspetto tecnico? Assolutamente no. Significa che dovresti investire tanto tempo nella costruzione di relazioni quanto ne investi nella scrittura del codice. Ho visto di persona come investire nelle relazioni possa portare a una migliore collaborazione, comunicazione e, alla fine, a un software migliore, anche in contesti in cui le competenze non sono al top. I tuoi colleghi non sono la tua famiglia, ma perché non potrebbero essere compagni di squadra in una partita che state giocando insieme?

2. Rendi il tuo codice leggibile.

Scrivi sempre il codice come se il tizio che dovrà manutenere il tuo codice sia un psicopatico violento che sa dove abiti. John F. Woods, 1991

Una delle lezioni più preziose che ho imparato è l’importanza di scrivere codice leggibile. La leggibilità del codice non è solo una preferenza; è una necessità. Quando si considera che tu e il tuo team leggerete il codice molto più spesso di quanto lo scriverete, il valore della chiarezza diventa innegabile.

All’inizio della mia carriera, ero concentrato esclusivamente sul rendere il mio codice più performante (e, lasciatemi dire, inutilmente complicato). Tuttavia, man mano che ho acquisito esperienza e ho assunto ruoli che hanno incluso la revisione del codice e il mentoring di sviluppatori junior, mi sono reso conto che la leggibilità ha un impatto molto maggiore sulla manutenibilità e sull’estensibilità di un codice rispetto all’efficienza o all’ingegnosità.

Dai un’occhiata all’esempio seguente:

const data = [1, 2, 3, 4, 5];

const result = data
  .reduce((acc, n) => {
    acc.push({ value: n * 2 });
    if (n * 2 > 5) {
      acc.pop();
    }
    return acc;
  }, [])
  .reduce((str, obj) => {
    return str + obj.value + ', ';
  }, '')
  .slice(0, -2);

console.log(result);

E ora guarda lo stesso codice, ma con un po’ più di attenzione alla leggibilità:

const data = [1, 2, 3, 4, 5];
const objects = [];
let resultString = '';

for (let i = 0; i < data.length; i++) {
  if (data[i] * 2 > 5) {
    continue;
  }
  objects.push({ value: data[i] * 2 });
}

for (let i = 0; i < objects.length; i++) {
  resultString += objects[i].value + ', ';
}

if (resultString.length > 0) {
  resultString = resultString.slice(0, -2);
}

console.log(resultString);

Anche senza commenti, puoi intuire chiaramente cosa sta succedendo nel secondo esempio. Il codice è suddiviso in pezzi più chiari e gestibili, rendendolo più facile da capire e mantenere. Il primo esempio, d’altra parte, è un blocco denso di codice che richiede una lettura attenta per decifrarne l’intento, anche se probabilmente è più performante e intelligente.

Naturalmente, lo stesso non si applica a tutti i contesti; gran parte del codice che ho scritto nelle mie librerie Open Source è più conciso e meno leggibile, poiché lo scopo è più ristretto e il pubblico è diverso. Ma quando si lavora su un prodotto con altri sviluppatori, la leggibilità dovrebbe essere una priorità assoluta.

Leggerai il tuo codice molto più spesso di quanto lo scriverai. E il tuo team lo leggerà ancora più spesso. Quindi, rendilo leggibile.

N.B. Il codice che ho mostrato è solo un esempio che mi è venuto in mente mentre scrivevo l’articolo, naturalmente esistono molti modi migliori per scrivere lo stesso codice.

3. Tu sei parte del business.

Se devo additare una cosa che vedo ripetutamente negli sviluppatori, è la mentalità di noi contro loro. Noi siamo gli sviluppatori, loro sono il business. Questa è una mentalità davvero pericolosa di cui anch’io sono stato colpevole in passato e nella quale a volte ricado ancora. Questo non potrebbe essere più lontano dalla verità.

Nel mondo di oggi, tutto ciò che vedi è realizzato da uno sviluppatore e ha del software che ci gira dentro. Il PC, il tablet o lo smartphone che usi per leggere questo articolo, l’auto che guidi, la macchina del caffè che usi ogni mattina, il frigorifero che apri ogni sera alla ricerca di qualcosa da mangiare - e, tra l’altro, non troverai mai nulla, dovresti andare a fare shopping più spesso - tutto ha un software che lo gestisce. E noi, come sviluppatori, siamo letteralmente coloro che fanno girare il mondo.

Quindi, perché dovremmo essere contro il business? Il business siamo noi. Siamo noi che facciamo funzionare il business. Siamo noi che facciamo crescere il business. Siamo noi a far sì che l’azienda abbia successo. E se lo dimentichiamo saremo noi a far fallire l’azienda.

Di solito, come sviluppatore sei tu la persona che conosce meglio il business in cui stai lavorando, poiché sei tu a farlo funzionare. Quindi, non aver paura di parlare apertamente, di condividere le tue idee, di sfidare lo status quo. Fai parte del business e hai voce in capitolo.

4. Trunk-based. Più o meno.

Lo sviluppo trunk-based è fantastico, ma non è per tutti.

Siamo nel mezzo della guerra Trunk-based contro Feature Branches. Ho visto entrambe le parti e ho visto entrambe le parti fallire. Ho visto entrambe le parti avere successo. Ho visto entrambe le parti fallire e avere successo allo stesso tempo.

Lo sviluppo trunk-based può aiutare a ridurre i problemi di integrazione, migliorare la collaborazione e accelerare il processo di sviluppo ma, come con qualsiasi pratica di sviluppo, non è una soluzione valida per tutti. Anche se aiuta con quanto citato sopra, può anche rendere più complicati i processi di revisione, riducendo di fatto il processo di sviluppo a causa di regressioni e bug.

Sebbene io sia favorevole allo sviluppo trunk-based, credo anche nell’apprendimento e nel miglioramento continui. I feature branch possono essere molto efficaci in determinate situazioni, dove il trunk-based risulta non essere all’altezza. La chiave è trovare il giusto equilibrio per il tuo team e il tuo progetto.

Secondo me, il miglior approccio è trovare un equilibrio tra i due, utilizzando i feature branch per funzionalità più grandi o modifiche che richiedono più tempo e collaborazione, e lo sviluppo trunk-based per modifiche più piccole e più semplici.

Per quella che è stata la mia esperienza, raccomando un approccio equilibrato: utilizzare i feature branch per funzionalità più grandi e più collaborative e lo sviluppo trunk-based per modifiche più piccole e semplici. Se utilizzi feature branch, cerca di fare merge entro un giorno. Se la tua funzionalità non può essere integrata entro la mattina successiva, allora dovresti metterci un feature flag.

In linea generale, sono contrario a seguire le regole in modo rigido, ma fortemente favorevole a comprenderle e ad adattarle alle proprie esigenze.

5. Testare abbastanza ti fa dormire meglio. Testare troppo ti darà il mal di testa.

Sono contrario a una coverage del 100% in una codebase, ma sono fortemente a favore del testare ogni percorso critico.

L’obiettivo non dovrebbe essere quello di coprire ogni singola riga di codice con un test, ma piuttosto di concentrarsi sui test delle parti del codice che contano di più.

Testare tutto ciò che conta significa identificare le funzionalità principali e i percorsi critici della tua applicazione e assicurarsi che siano testati in modo approfondito. Queste sono le aree in cui i bug avrebbero il maggior impatto, quindi garantire la loro correttezza è fondamentale. Ad esempio, in un’applicazione di e-commerce, testare il processo di checkout è molto più cruciale che testare dettagli minori dell’interfaccia utente.

Kent Beck una volta disse:

Non sono un grande programmatore; sono solo un buon programmatore con ottime abitudini.

Beh, penso che il testing sia una di quelle abitudini che può fare una differenza significativa nella qualità del tuo software. Ma per favore, non testare tutto. Non farti questo. Finirai con una suite di test che impiega ore per essere eseguita, è impossibile da mantenere e si rompe ogni volta che cambi una riga di codice.

6. Monitora il debito tecnico, e pagalo quando è ora di farlo.

Il debito tecnico è un fatto della vita nello sviluppo software. La chiave è gestirlo in modo efficace.

Mi trovo a dover affrontare il debito tecnico ogni giorno. E sai perché? Perché il codice che scrivo oggi sarà il fardello di domani.

E il codice legacy alle volte diventa debito tecnico.

Il debito tecnico non è intrinsecamente cattivo. È un sottoprodotto naturale del processo di sviluppo, e può essere uno strumento prezioso per bilanciare la necessità di velocità con la necessità di qualità. La chiave è gestire il debito tecnico in modo efficace, tracciandolo, prioritizzandolo e pagandolo in modo continuativo.

Come aspirante Software Architect, ho studiato gli Architecture Decision Record, e ho scoperto che sono un ottimo strumento per tracciare anche il debito tecnico. Documentando le decisioni che prendiamo e i compromessi che accettiamo, possiamo creare un registro del nostro debito tecnico e usarlo per informare le decisioni future, o per ripagarlo quando è il momento.

7. Test unitari, test di integrazione e test end-to-end.

☢️ Attenzione: contenuto opinionato in arrivo. ☢️

Riguardo alla famosa piramide dei test, preferisco ribaltarla quando lavoro su un grande progetto: credo che i test end-to-end dovrebbero costituire la maggior parte della tua base di test, seguiti dai test di integrazione e dai test unitari. Questo perché i test end-to-end sono la cosa più vicina a un’interazione utente reale e significativa.

Naturalmente, raggiungere questo obiettivo può essere difficile, specialmente in codebase legacy, caso in cui ricorrerei ai test di integrazione seguiti dai test end-to-end e ai test unitari.

Odio i test unitari? Assolutamente no. Penso che siano un ottimo strumento per garantire che il tuo codice sia modulare e testabile, ma quando si lavora su una grande codebase, l’integrazione tra i componenti è la cosa più vicina che puoi ottenere ai test end-to-end e, di conseguenza, all’interazione reale dell’utente. D’altra parte, i test unitari sono una parte fondamentale di ogni pacchetto e libreria che sviluppo e mantengo, poiché garantiscono la protezione contro i casi comuni e gli edge case.

Inoltre, credo fermamente che il testing sia una questione di qualità, non di quantità.

Non mi credi? Ti do alcuni numeri: Nella codebase su cui lavoro attualmente, abbiamo più di 3000 asserzioni nei nostri test di integrazione, 800 di queste sono nel processo di pagamento. Meno di 50 di queste sono necessarie per coprire l’happy path, il resto sono edge case e gestione degli errori.

8. La documentazione è importante, ma non puoi scriverla da solo.

La migliore documentazione è quella scritta dal team, per il team.

Un team non è una cosa che puoi fingere di essere da solo, e lo stesso vale per la documentazione. Coinvolgendo il team nel processo di documentazione, puoi sfruttare la conoscenza e l’esperienza collettiva dei membri del team per creare una documentazione completa e accurata che rifletta la comprensione condivisa del team.

Inoltre, coinvolgere il team nel processo di documentazione può favorire un senso di proprietà e responsabilità. Quando i membri del team contribuiscono alla documentazione, sono più propensi a farvi riferimento, aggiornarla e mantenerla nel tempo.

Ovviamente, team non significa tutti, sempre. Dovresti coinvolgere le persone giuste nel processo di documentazione, quelle che hanno più conoscenza ed esperienza nelle aree che vengono documentate, e non aver paura di chiedere una riscrittura o una chiarificazione se qualcosa non è chiaro o accurato.

Infine, la documentazione non dovrebbe essere un’attività separata dallo sviluppo del software, ma dovrebbe essere integrata nel processo di sviluppo stesso. La documentazione dovrebbe essere scritta insieme al codice, in modo che sia sempre aggiornata e accurata.

Per essere chiari: non intendo dire che dovresti scrivere commenti nel codice. Intendo dire che dovresti scrivere la documentazione assieme al codice. I commenti sono un ottimo strumento per spiegare perché hai scritto un pezzo di codice, ma non sostituiscono la documentazione. La documentazione dovrebbe fornire una panoramica di alto livello della codebase o della funzionalità che si sta rappresentando, spiegare come i diversi componenti interagiscono tra loro e fornire indicazioni su come utilizzare e mantenere il codice.

9. Stabilità prima dell’innovazione.

Innovare per il gusto di farlo è un lusso che pochi possono permettersi. E anche quando puoi permettertelo in prima istanza, il prezzo che pagherai lungo il cammino sarà troppo alto.

Tutti amano lavorare sulle ultime tecnologie e con gli strumenti più in voga, ma la realtà è che la stabilità è più importante dell’innovazione.

Business is king, e l’obiettivo principale di qualsiasi progetto di sviluppo software è fornire valore al business. Ciò significa che la stabilità, l’affidabilità e la manutenibilità dovrebbero sempre avere la precedenza sull’innovazione. Sebbene sia essenziale rimanere aggiornati sulle ultime tecnologie e tendenze, è altrettanto importante garantire che il tuo software sia stabile, affidabile e mantenibile.

Quindi, dovremmo smettere di innovare? Assolutamente no. L’innovazione è essenziale per la crescita e il progresso, ma dovrebbe essere fatta con cura e considerazione.

Migrare da PHP a Rust solo perché fa figo non è una buona ragione per innovare. Farlo perché le tue bollette di infrastruttura stanno aumentando, e Rust può aiutare la tua azienda a risparmiare denaro, lo è.

Spostare tutto il tuo codice da JavaScript a TypeScript solo perché è il nuovo ragazzino figo del quartiere non è una buona ragione per innovare. Farlo perché può aiutare il tuo team a scrivere codice più affidabile e manutenibile, lo è.

Inoltre, l’innovazione porta a sfide, e le sfide portano ad errori. Quindi, se vuoi innovare, sii pronto a fallire e a pagare per questo. Fallire non è la fine del mondo e può essere un’esperienza di apprendimento preziosa, ma non è qualcosa che dovresti incoraggiare solo per il gusto di farlo.

10. Reinventa la ruota, a volte.

Reinventare la ruota è di gran lunga il modo migliore per fallire, ma anche il modo migliore per imparare.

Ho perso il conto delle volte in cui ho costruito il mio form builder, la mia libreria di state management, il mio router, il mio tutto. E ho perso il conto delle volte in cui ho fallito. Eppure, non mi pento di nessuno di quei fallimenti.

La prima volta che ho costruito il mio form builder, ho completamente sbagliato la gestione dello stato, la validazione, la personalizzazione dei campi e la gestione degli errori.

Cosa hai detto? Queste sono le funzionalità principali di un form builder? Sì, lo sono. E ho fallito in tutte. La volta successiva, ho sbagliato solo 3 di esse. Poi 2. Poi 1. Poi nessuna. Sono diventato un form builder ninja. E non appena ci sono arrivato, ho iniziato a usare una libreria che faceva tutto questo per me.

Cosa? Hai sprecato tutto quel tempo per imparare a farlo e ora stai usando una libreria? Sì, certo. Ma ora so come funziona, e ogni volta che la uso, lo faccio con fiducia e conoscenza.

Alcuni anni fa, ho costruito il mio framework JavaScript. Era così brutto che il parser dell’URL era composto da più di 1.000 righe di codice. Ma sai cosa? Ho imparato molto su come funzionano i framework, come gestiscono cose come il routing, il rendering dei componenti, l’emissione di eventi. E ora, ogni volta che uso Angular, o Vue, o React, non ho paura della reattività o della gestione dello stato, perché so come funziona.

11. Non puoi sapere tutto, ma puoi sapere come trovare tutto.

I migliori sviluppatori non sono quelli che sanno tutto, ma quelli che sanno come trovare tutto.

La prima cosa che consiglio a ogni junior developer che incontro è: impara a trovare il problema, e, più in generale, impara a cercare. La capacità di cercare efficacemente è una competenza preziosa che può aiutarti a risolvere problemi e imparare cose nuove.

Iniziamo con la parte “risolvere i problemi”. Quando ti trovi di fronte a un problema che non sai come risolvere, dovresti fare tre semplici cose:

  1. dimentica la prima soluzione che ti è venuta in mente, perché il 99% delle volte è sbagliata;
  2. scomponi il problema, perché 9 volte su 10 il problema non è l’intero sistema di pagamento che va giù ogni volta che arriva una nuova transazione, ma probabilmente una variabile non controllata o un campo cancellato;
  3. chiedi aiuto a una rubber duck. Una vasta quantità di problemi si risolvono da soli solo quando li svolgi ripetendo il processo ad alta voce. Se non funziona, chiedi a un collega. Se non funziona… cerca!

Aspetta cosa?! Essere un esperto di ricerca su Google può essere una competenza preziosa? Sì, lo è. Sapere come cercare efficacemente può aiutarti a trovare risposte alle tue domande, scoprire nuovi strumenti e tecnologie e imparare dalle esperienze degli altri. Inoltre, è improbabile che tu sia la prima persona a incontrare uno specifico problema, quindi è probabile che qualcun altro abbia già posto la stessa domanda e ricevuto una risposta che puoi adattare alle tue esigenze.

12. Non esistono silver bullet.

Tutto nell’Architettura del Software è un compromesso. Mark Richards e Neal Ford, 2020.

Se pensi di aver trovato una soluzione che può adattarsi a più di un paio di problemi, significa solo che non hai capito il problema.

Questa è di gran lunga la lezione più importante che ho imparato in questi miei primi 10 anni come sviluppatore.

Non esistono soluzioni universali. Non esistono soluzioni che funzionino per tutti i problemi. Non esistono pillole magiche che risolvano tutti i tuoi problemi.

Pensa ai trade-off ogni volta che prendi una decisione. Pensa ai pro e ai contro di ogni opzione. Pensa all’impatto della tua decisione sul progetto, sul team e sul business. Pensa alle conseguenze a lungo termine della tua decisione. E se quei trade-off sono soddisfacenti per te, procedi.

Conclusioni.

Chissà cosa mi riserverà il prossimo decennio? Sicuramente, posso intuire cosa sarà: AI, sicuramente. Ma il come è una questione totalmente diversa.

Un po’ di ringraziamenti.

Centinaia di persone hanno forgiato questo articolo e mi hanno aiutato a crescere negli ultimi 10 anni. Non posso ringraziarle tutte, ma sento il bisogno di ringraziarne alcune in particolare.

Flavia, mia moglie, che ha avuto la pazienza di ascoltarmi parlare di codice per ore.
Lorenzo, che mi stimola ogni giorno ad essere un uomo, e un padre, migliore.
Christian, mio fratello, che mi ha insegnato come montare una scheda grafica e come scrivere la mia prima riga di codice.
Michele, il mio primo mentore, che mi ha insegnato a pensare in modo critico e a risolvere i problemi.
Max, un caro amico, che mi ha insegnato a essere una persona migliore, sia dentro che fuori dal lavoro.
Serena, che è sempre stata lì, pronta ad aiutarmi quando ne avevo bisogno.
Enry, che ha creduto in me fin dal primo momento.

Grazie, Michael.

P.S. Questo articolo è stato scritto con il supporto dell'intelligenza artificiale.
Torna alla home