Studio Informatica e Tecnologia al Tec de Monterrey e sono studente dell’Apple Developer Academy a Napoli, Italia. Quando sono arrivato all’Academy sapevo programmare, avevo basi di sviluppo web e sviluppo full-stack, ma non avevo mai costruito un prodotto reale. Al di fuori della mia esperienza in una startup, non avevo mai dovuto pensare agli utenti, all’infrastruttura, a come scalare quello che stavo scrivendo o a come lavorare con un team multidisciplinare dove non tutti parlano la tua lingua.
Questo blog è una retrospettiva onesta di ciò che ho imparato lungo questo percorso. Non è un tutorial né un riassunto tecnico. È la mia riflessione come ingegnere del software sulle decisioni, gli errori e gli apprendimenti che mi hanno segnato durante la mia esperienza all’Academy.
1. Technical Growth
Il percorso del mio sviluppo tecnico durante questo periodo all’Apple Developer Academy è stato molto ampio. Fin dall’inizio sapevo che volevo sviluppare prodotti, non progetti. Questa è sempre stata la mia intenzione, e quella stessa intenzione è ciò che mi ha portato a esplorare molti strumenti, sviluppare competenze e crescere come ingegnere del software.
Lo stack che ho costruito
Oltre al linguaggio Swift e al framework SwiftUI, ho potuto esplorare framework Apple che non avrei mai immaginato di toccare così presto. Nel mio ultimo progetto, Unnoticed, ho guidato la decisione di implementare la crittografia end-to-end per le fotografie degli utenti. È stata identificata la necessità, sono stati definiti i requisiti di sicurezza e il compito è stato assegnato. Capire il problema a livello architetturale, sapere cosa serve e perché, e delegare l’implementazione in modo efficace è un’abilità importante che ho imparato ultimamente.
Lato server, ho costruito uno stack completo: Node.js con Express.js, PostgreSQL come database, Redis per le code di lavoro, autenticazione con JWT e archiviazione file su Cloudflare R2. Ho progettato l’API REST e la logica del ciclo di vita del prodotto.
Ho anche sviluppato landing page per le applicazioni in Astro, un framework che ho scoperto quest’anno e che mi ha conquistato completamente. È davvero ottimizzato per pagine che servono contenuto statico e per il SEO. Inclusa la landing di Unnoticed nel nostro monorepo.
Un mondo di tecnologie non direttamente legate all’ecosistema Apple, ma che senza dubbio arricchiscono il mio stack e la mia esperienza come ingegnere.
L’architettura come pilastro
L’architettura del software è diventata un pilastro fondamentale per me, soprattutto in quest’era in cui puoi avere più agenti IA che programmano contemporaneamente. Pianificare i requisiti software, definire standard di qualità, configurare ambienti di sviluppo locali e nel cloud, progettare pipeline di CI/CD: li considero indispensabili per costruire software di qualità.
In Unnoticed, per esempio, una delle prime decisioni che ho preso è stata strutturare il progetto come un monorepo. Perché? Perché avevamo tre pezzi che dovevano condividere la configurazione e rimanere sincronizzati: l’app iOS, il backend e la landing page. Un monorepo ci ha permesso di avere un unico repository dove viveva tutto, condividere tipi tra progetti e avere un’unica fonte di verità per la configurazione dell’ambiente di sviluppo, Docker e CI/CD. Inoltre, ha facilitato a qualsiasi membro del team di avviare l’intero ambiente di sviluppo con un solo comando.
Per il flusso di lavoro del team, abbiamo implementato Scrum usando Jira per gestire gli sprint, organizzare il backlog e monitorare i task. Avere un processo strutturato ci ha aiutato a dividere il lavoro, dare priorità alle feature e mantenere visibilità su cosa stava facendo ciascuno. Abbiamo anche configurato flussi di CI/CD, che ci hanno permesso di automatizzare le validazioni prima di integrare il codice e mantenere la qualità del repository.
Capire che l’architettura del software è stata e sarà sempre indispensabile per costruire software, specialmente in questi tempi di IA, è stato un punto di svolta per me. Il vibe coding è di tendenza e, a dire il vero, è ottimo per portare un prototipo decente alla realtà in tempi record.
Ma il problema sta proprio nel fatto che ciò che costruiscono quegli agenti sono prototipi. Non sono pensati per essere scalabili, mantenibili, affidabili e sicuri. O almeno, non quelli che ho visto finora.
È lì che l’architettura del software e il pensiero critico diventano cruciali. Capire, se non dall’inizio, il prima possibile, il codice che hai e il codice che l’IA genera per te. Altrimenti il codice si sente fragile ed è complicato aggiungere una nuova feature o persino correggere un bug senza rompere la produzione. Sto ancora imparando come ottenere il massimo dall’IA come strumento. E man mano che avanzo nella mia carriera, mi rendo conto che voglio sempre più capire il basso livello della tecnologia che sta dietro al software che costruisco.
Il contrasto con l’università
Il contrasto tra l’università e l’Academy si riassume in questo: prima, costruire software per me era un progetto scolastico, qualcosa limitato all’ambito del corso che nessuno avrebbe mai davvero usato. Ora costruire software significa pensare al futuro di ciò che sto costruendo, al fatto che sarà usato da persone con bisogni reali.
Ci sono così tante tecnologie là fuori, così tanti framework, così tanti linguaggi, così tante cose da imparare. Dopo aver percorso questa strada, mi rendo conto che voglio imparare e costruire con un’intenzione chiara. Non importa quante nuove tecnologie, nuovi concetti o nuovi strumenti debba imparare, perché finché capisco il perché di ciò che sto costruendo, so che mi spingerà a farlo nel miglior modo possibile. E sento anche che mi renderà un ingegnere migliore.
2. Engineering Challenges & Decisions
Il mio caso più recente e completo per parlare di sfide ingegneristiche è Unnoticed. È un’app collaborativa di fotografia: gli utenti creano viaggi, scattano foto in round basati su prompt creativi assegnati per ruolo, e votano per gli scatti migliori. Costruirla mi ha messo di fronte a decisioni tecniche che non avevo mai dovuto prendere in un progetto reale.
Requisiti e architettura da zero
Prima di scrivere una sola riga di codice, mi sono seduto con il team a raccogliere i requisiti. Che esperienza volevamo creare? Di cosa aveva bisogno l’utente? Cosa era tecnicamente fattibile nel tempo che avevamo? Da lì è uscita la definizione dell’architettura: un’app nativa in SwiftUI con pattern MVVM, un backend in Express con Prisma e PostgreSQL, e archiviazione immagini nel cloud.
La decisione di usare un monorepo è stata mia. L’ho proposta perché volevo che il team avesse un unico posto dove vivesse tutto. Questo ha eliminato la frizione di mantenere più repository sincronizzati e ha facilitato a tutti la comprensione dell’architettura completa del progetto.
Cloudflare R2 rispetto ad Amazon S3
Per l’archiviazione delle immagini avevamo bisogno di un servizio di storage nel cloud che fosse accessibile nel prezzo e facile da configurare. Ho scelto Cloudflare R2 rispetto ad Amazon S3 per tre ragioni:
- Prezzo: R2 non addebita per l’uscita dei dati (bandwidth), che è dove S3 diventa costoso rapidamente quando servi immagini agli utenti.
- Accessibilità: La configurazione è più semplice, l’interfaccia di Cloudflare è più intuitiva e la curva di apprendimento è minore rispetto all’ecosistema AWS.
- Compatibilità: R2 è compatibile con l’API di S3, il che significa che se un giorno dovessimo migrare, il cambiamento sarebbe minimo.
Il flusso di caricamento foto lo abbiamo progettato in modo che il server non toccasse mai i byte dell’immagine: il client richiede un permesso temporaneo di caricamento, carica direttamente nello storage e poi notifica il backend. Questo riduce il carico del server e migliora la velocità per l’utente.
La cache delle immagini
Una sfida importante di prestazioni è stata il caricamento delle immagini. In una galleria con decine di foto, scaricare ogni immagine dalla rete ogni volta che l’utente la visualizzava rendeva l’esperienza lenta e inaccettabile.
La soluzione è stata implementare un sistema di cache intelligente: la prima volta che una foto viene scaricata, viene salvata in cache (sia in memoria che su disco). Le volte successive in cui l’utente vede quella foto, si carica istantaneamente dalla cache senza bisogno di scaricarla di nuovo.
Abbiamo anche implementato la deduplicazione delle richieste: se più parti dell’app richiedono la stessa immagine contemporaneamente, viene effettuato un solo download e tutte condividono il risultato. Questo ha migliorato significativamente l’esperienza utente nella navigazione della galleria foto.
Crittografia end-to-end
Una delle sfide più ambiziose del progetto è stata la crittografia delle foto. La premessa che abbiamo definito come team era semplice: il server non deve poter vedere le foto degli utenti. Solo i partecipanti al viaggio dovrebbero poterle vedere.
Il mio ruolo qui è stato identificare questa esigenza di privacy, definire i requisiti ad alto livello e assegnare il compito. L’implementazione è stata realizzata da un compagno di team, ma la decisione architetturale che il server fosse zero-knowledge (che facilitasse solo lo scambio di chiavi senza mai apprenderne il contenuto) è stata una decisione di team che ho promosso.
Il risultato: le foto vengono cifrate prima di lasciare il telefono e ciò che arriva allo storage nel cloud è indecifrabile senza la chiave del viaggio. Solo i dispositivi dei partecipanti possono vedere le immagini. È una delle feature di cui sono più orgoglioso come tech lead, perché dimostra che saper delegare e definire requisiti chiari è tanto importante quanto implementare.
Con la crittografia attiva, il sistema di cache che già avevamo è stato adattato per gestire foto crittografate: decifra l’immagine una sola volta e memorizza il risultato decifrato, evitando di ripetere il costoso processo di decifratura ad ogni visualizzazione.
La decisione di non avere login
Una decisione di prodotto che è stata anche ingegneristica: abbiamo deciso di non implementare login né registrazione nel prototipo. Volevamo che l’esperienza fosse il più fluida possibile, che l’utente potesse creare un viaggio o unirsi a uno in pochi secondi. Il limite è ovvio: meno controllo sull’informazione dell’utente e sulla persistenza della sua identità. Ma per un prototipo il cui obiettivo era validare l’esperienza di gioco, dare priorità alla velocità di onboarding è stata la decisione giusta.
Regole di sviluppo e qualità del codice
Gli errori più comuni e frequenti non sono stati bug tecnici, sono stati di collaborazione e qualità del codice. Dopo troppi merge conflict che ci costavano ore, ho proposto al team di stabilire regole di sviluppo, che abbiamo documentato nel repository:
- Sincronizzare sempre il proprio branch con il repository remoto prima di pushare le modifiche.
- Risolvere i conflitti preferendo la combinazione di entrambe le sezioni piuttosto che sostituirne completamente una.
- Seguire standard di qualità nel codice per non impattare il lavoro degli altri.
Stiamo ancora lavorando per rendere questo processo più efficiente e migliorarlo, ma credo che tutti ci siamo portati a casa una buona esperienza imparando a sviluppare davvero in team.
3. Collaboration & Product Development
Lavorare in team all’Academy è stata una delle esperienze più arricchenti che ho avuto finora. A dire il vero, anche una sfida molto grande.
La barriera linguistica
Partendo dalla barriera linguistica. Anche se la comunicazione è in inglese, non essendo la mia lingua madre né quella del mio quotidiano, non sempre mi è stato facile comunicare il mio punto di vista o le mie opinioni in modo assertivo o chiaro. Ma è stata una bella sfida e sicuramente un’ottima pratica per i futuri team internazionali che desidero avere.
Guidare dalla tecnica e oltre
In Unnoticed ho assunto la leadership del team, non solo nell’aspetto tecnico ma nell’organizzazione generale del progetto. Sono stato io a proporre l’architettura del monorepo, a configurare l’ambiente di sviluppo con Docker e lo script di inizializzazione, a definire le convenzioni Git e le regole di qualità del codice, e a stabilire Scrum come metodologia di lavoro con Jira.
Ma guidare non è stato solo prendere decisioni tecniche. È stato anche facilitare le riunioni di sprint, aiutare a dividere il lavoro in modo che ognuno potesse contribuire dai propri punti di forza, mediare quando c’erano disaccordi sulla direzione del prodotto e assicurarmi che il team rimanesse motivato e allineato. Ho imparato che essere il tech lead non significa fare tutto: significa creare le condizioni perché il team possa lavorare bene.
Un team internazionale
Condividere la mia cultura con i miei compagni e imparare dalle loro è stato davvero incredibile. Conoscere persone dall’Italia, Iran, Brasile, Russia, Vietnam, India, Egitto, Spagna e altro ancora. Ognuno portava una prospettiva diversa non solo sulla tecnologia, ma su come si risolvono i problemi, come si comunicano le idee e cosa significa “qualità”.
Prospettive multidisciplinari
Condividere prospettive con persone che non sono del mio campo mi ha anche permesso di ampliare il mio punto di vista. È stato fondamentale avere designer e profili business per prendere decisioni sulla comunicazione dell’app, il flusso utente, modelli di business e organizzazione del progetto. Un ingegnere da solo non costruisce un buon prodotto. Un team sì.
La differenza con l’università
La differenza più grande tra questa esperienza e l’università è che qui non c’era nessuno che ti seguiva né che ti dava date di consegna specifiche e linee guida. Anche se avevamo una presentazione finale, la parte più impegnativa è stata fare un piano con il team e attenersi ad esso e alle scadenze che noi stessi abbiamo definito. Quella responsabilità condivisa è ciò che più assomiglia al mondo reale.
4. Professional Growth
Da progetti scolastici a prodotti reali
Il mio modo di lavorare è cambiato fondamentalmente. Prima pensavo a consegnare un compito. Ora penso a costruire qualcosa che qualcuno userà. Quella differenza sembra sottile ma cambia tutto: come progetti l’architettura, come gestisci gli errori, come pensi alle prestazioni, come documenti il tuo codice.
Imparare a prendere decisioni con informazioni incomplete
All’università, i problemi arrivano con specifiche chiare. All’Academy, ho dovuto imparare a prendere decisioni con informazioni incomplete e a convivere con il trade-off. R2 o S3? Login o senza login? Monorepo o multi-repo? Crittografia end-to-end o fidarsi del server? Nessuna di queste decisioni ha una risposta “corretta”. Tutte hanno trade-off, e imparare a valutarli rapidamente e andare avanti è stato uno degli apprendimenti più preziosi.
Comunicare idee tecniche
Ho imparato che saper programmare non basta. Devi saper comunicare cosa stai costruendo e perché. Documentare l’architettura del progetto, scrivere specifiche tecniche affinché un compagno potesse implementare esattamente ciò che il frontend si aspettava, o spiegare a un designer perché una decisione tecnica influenzava l’esperienza utente. Tutto questo fa parte del lavoro di un ingegnere.
L’IA come strumento, non come sostituto
Ho usato strumenti di IA come Claude Code in tutti i miei progetti all’Academy. In iDeary, l’ho usato per generare la struttura dell’onboarding dal mio codice esistente. In Unnoticed, per iterare su implementazioni del backend e il protocollo di crittografia. Ma in ogni caso, la logica del prodotto, le decisioni architetturali e il design dell’esperienza sono stati miei e del team.
La lezione che porto con me è che l’IA è straordinariamente utile per eseguire più velocemente, ma non sostituisce il giudizio ingegneristico. Sapere cosa costruire, perché e come strutturarlo perché sia mantenibile resta lavoro umano. E credo che questo diventerà sempre più prezioso, non meno.
Cosa viene dopo
Sono nella fase finale dell’Apple Developer Academy e ne esco con uno stack tecnico molto più ampio di quello con cui sono arrivato, ma soprattutto con una mentalità diversa. Voglio continuare a costruire prodotti che risolvano problemi reali, voglio continuare a guidare team tecnici e voglio continuare ad approfondire il basso livello della tecnologia che uso ogni giorno. Perché finché capisco il perché di ciò che sto costruendo, so che mi spingerà a farlo nel miglior modo possibile.