Benvenuto alla prima lezione del corso di Data and System Architecture. Ottanta lezioni, si parte da una singola VM che esegue uno script Python e si finisce con un sistema globale multi-region che non crolla quando uno dei suoi data centre prende fuoco. Attraverseremo in ordine la parte caotica nel mezzo: come un sistema cresce da una macchina a due, da due a una piccola flotta, da una piccola flotta a una region, e da una region al pianeta intero. Lungo la strada copriremo i trade-off, le cose che non puoi più “non-decidere” una volta decise, e il sorprendente numero di fallimenti che nascono perché ci si dimentica che la rete, in realtà, non è affidabile.
Questo corso si appoggia molto a tre testi che, secondo me, ogni persona seria nei sistemi dovrebbe leggere: “Designing Data-Intensive Applications” di Martin Kleppmann, “Building Microservices” di Sam Newman, e l’SRE workbook di Google. Nessuno dei tre è riprodotto qui dentro. Prendo in prestito da loro vocabolario, qualche esempio occasionale, e il gusto generale di come ragionare sui sistemi distribuiti, ma le lezioni sono scritte da zero con gli esempi ricorrenti e l’angolazione che trovo utile. Se finisci questo corso e vuoi andare più a fondo, sono lì che devi andare.
Prima di toccare un solo diagramma di architettura, dobbiamo rispondere a una domanda che suona sospettosamente da filosofia universitaria e che si rivela davvero importante quando sei seduto in una riunione: cos’è l’architettura del software, di preciso? Vale la pena metterla a fuoco perché la definizione sbagliata ti porterà a discutere delle cose sbagliate per il resto della tua carriera.
Le definizioni classiche, e perché non aiutano
La definizione più citata, da manuale, viene da Bass, Clements e Kazman in “Software Architecture in Practice”:
L’architettura del software di un sistema è l’insieme di strutture necessarie per ragionare sul sistema, che comprende elementi software, relazioni tra di essi, e proprietà di entrambi.
È una frase perfettamente accurata. È anche, per essere franchi, inutile quando sei in piedi davanti a una lavagna alle 16 di un martedì cercando di decidere se usare Postgres o DynamoDB. Ti dice che l’architettura è “strutture ed elementi e relazioni e proprietà”, che è un po’ come dire che un pasto è “ingredienti e combinazioni e sapori e presentazioni.” Tecnicamente vero. Non ti aiuta a cucinare la cena.
Martin Fowler, che ha riflettuto su questo più della maggior parte, ha rinunciato a cercare una definizione pulita e ha invece citato Ralph Johnson:
L’architettura riguarda le cose importanti. Qualunque esse siano.
Questa è più vicina alla verità ed è anche evidentemente circolare. Cos’è importante? Importante per chi? Quando? Il punto di Fowler nel citarla è che gli architetti su un progetto reale finiscono per concordare, in gran parte intuitivamente, su quali siano le “cose importanti”, ed è su quelle che spendono il loro tempo. La definizione è una descrizione di cosa fanno davvero gli architetti, non una procedura per capire cosa fare.
Ho letto ogni variante di questa definizione che esista, e tutte condividono un unico problema. Descrivono l’architettura come una cosa (un insieme di strutture, un insieme di decisioni, un insieme di cose importanti) senza dirti come riconoscere se una determinata scelta che stai per fare è architetturale o no. E riconoscerlo è tutta la skill. Se sei capace di individuare la decisione architetturale in un mucchio di scelte, sai su quali rallentare, quali dibattere, quali scrivere nero su bianco, e quali invece basta spedire e via.
Quindi ecco la definizione operativa che userò per le prossime ottanta lezioni.
La definizione operativa: l’architettura è ciò che è costoso cambiare
L’architettura è l’insieme delle decisioni che è costoso cambiare in seguito.
Tutto qui. Se una decisione è difficile da cambiare una volta in produzione, è architetturale. Se è facile da cambiare, non lo è.
Le “cose importanti” di Ralph Johnson sono solo un modo meno preciso di dire la stessa cosa. La ragione per cui una decisione è “importante” in senso architetturale è che invertirla ti costerebbe settimane, mesi o anni. Le decisioni che non hanno questa proprietà non sono architetturali, non importa quanto sembrino pesanti sul momento. La scelta della convenzione di naming delle variabili sembra pesante in una code review e non è architetturale. La scelta del linguaggio di programmazione di un servizio sembra di routine la prima settimana ed è assolutamente architetturale.
Questa definizione ha un corollario utile: le decisioni architetturali non sono lo stesso delle buone decisioni di ingegneria, e non sono sempre le stesse decisioni su cui passi più tempo a discutere. Alcune decisioni architetturali si prendono in cinque secondi perché la risposta è ovvia (“useremo Postgres perché qui dentro tutti conoscono Postgres”). Alcune decisioni non architetturali consumano una settimana di dibattito (“quale dovrebbe essere la convenzione di naming dei campi JSON”). La quantità di discussione non è un segnale affidabile. Lo è il costo del ripensamento.
Scelte architetturali contro scelte di design
Concretizziamo. Ecco una lista di scelte che un team potrebbe fare in un progetto tipico. Alcune sono architetturali nel nostro senso, altre no. Passale in rassegna con il test “costoso da cambiare?” in testa.
-
Scelta del database. Architetturale. Migrare da Postgres a DynamoDB tre anni dentro al progetto è un lavoro di mesi. Dovrai riscrivere il data access layer, ridisegnare gli schemi, rifare gli indici, cambiare i pattern transazionali, riformare tutti, e rieseguire i test di performance. La gente lo fa e se lo ricorda per sempre.
-
Scelta del linguaggio di programmazione di un servizio. Architetturale. Riscrivere un servizio Java da 50.000 righe in Go non è qualcosa che fai nel weekend. Anche riscrivendolo gradualmente con uno strangler pattern è un anno della vita di qualcuno.
-
HTTP sincrono contro un event bus per la comunicazione service-to-service. Architetturale. Tutta la forma con cui i servizi interagiscono, falliscono, fanno retry, e si osservano a vicenda dipende da questo. Cambiare uno per l’altro tocca ogni endpoint del sistema.
-
Deployment single-region contro multi-region. Estremamente architetturale. Le assunzioni di latenza, il modello di consistency, il modo in cui gestisci il failover, e il modo in cui paghi il cloud provider cambiano tutti.
-
Scelta tra REST e GraphQL per una API pubblica. Per lo più architetturale, perché una volta che i client esterni dipendono da quella API, deprecarla richiede anni di email cortesi.
-
Nome di un metodo in una classe interna. Non architetturale. Rinominalo, lancia i test, spediscilo.
-
Convenzione di naming per le variabili d’ambiente. Non architetturale. Fastidioso da cambiare ma economico.
-
Se usare un logging framework o
printstatements durante il prototipazione iniziale. Non architetturale. Passerai a un framework il giorno in cui inizi a girare in staging. -
Scelta del provider di auth (Auth0 contro Cognito contro Keycloak contro farsela in casa). Architetturale. Migrare gli utenti tra sistemi di auth è un progetto vero, in parte perché non puoi migrare gli hash delle password tra provider senza forzare ogni utente a fare reset.
-
L’esatta policy di retry-and-backoff per una singola chiamata HTTP. Quasi mai architetturale. Modificala il martedì, deployala il mercoledì, guardala lavorare meglio.
Il pattern: tutto ciò che tocca schema, contratto, protocollo, linguaggio, topologia di deployment, identità, o ownership dei dati tende a essere architetturale. Tutto ciò che tocca nomi, formati, helper interni, o algoritmi locali di solito non lo è. Ci sono eccezioni. La lista è un punto di partenza, non una checklist.
La scala dell’irreversibilità delle decisioni
Il taglio “costoso contro economico da cambiare” è un primo setaccio utile, ma in pratica il costo del ripensamento è un continuum, non un binario. Trovo utile pensarlo come una scala con almeno tre pioli:
Reversibile in un giorno. Un nuovo feature flag. Un cambio di livello di logging. Un indice SQL. Una variabile rinominata. Un bump di versione di una libreria, assumendo che nulla si rompa. Puoi spedire il cambiamento la mattina e fare il rollback nel pomeriggio se va male.
Reversibile in un trimestre. Un nuovo servizio interno. Lo swap di una tecnologia di cache con un’altra. Il passaggio da una region cloud a un’altra dentro lo stesso provider. Il passaggio da un ORM a un altro, in una codebase abbastanza piccola da rendere “riscrivere il data layer” fattibile dentro un trimestre. Questi cambiamenti hanno bisogno di un piano di progetto e di almeno un ingegnere che ci lavora per settimane. Si possono rollback, ma non si fanno rollback con leggerezza.
Reversibile in un anno, o mai. Scelta della famiglia di database. Scelta del cloud provider. Scelta del sistema di autenticazione. Scelta della forma dell’API pubblica. Scelta della topologia di deployment geografico. Scelta dei confini di ownership dei dati tra team. Una volta che queste sono in produzione con una customer base sopra, uscirne è uno sforzo aziendale maggiore. Alcune non sono nemmeno tecnicamente reversibili senza perdere dati o rompere contratti; ci convivi e basta, girandoci intorno.
L’abilità dell’architettura è riconoscere su quale piolo si trova una determinata decisione, e calibrare la serietà del processo sul piolo. Una decisione da piolo-giornaliero può essere presa da un ingegnere in quindici minuti. Una decisione da piolo-trimestrale merita un architecture decision record (ADR) e un paio d’ore di design. Una decisione da piolo-annuale merita ricerca vera, prototipazione vera, e un piccolo panel di persone che hanno fatto scelte simili in passato e concordano che è la chiamata giusta.
L’errore classico dei team junior è trattare ogni scelta come una decisione da piolo-annuale e non spedire mai nulla. L’errore classico dei team senior sotto pressione di scadenza è trattare le decisioni da piolo-annuale come se fossero da piolo-trimestrale perché sul momento non sembrano un grande affare. Entrambi sono sbagliati. Il lavoro è sapere qual è quale.
flowchart LR
A[Easy to change<br/>hours to days] --> B[Medium<br/>weeks to a quarter]
B --> C[Hard<br/>quarter-plus to never]
A1[variable name<br/>log level<br/>retry policy<br/>feature flag] -.examples.-> A
B1[new internal service<br/>cache swap<br/>region within cloud<br/>ORM change] -.examples.-> B
C1[database family<br/>cloud provider<br/>auth provider<br/>public API shape<br/>multi-region topology] -.examples.-> C
Il piolo di mezzo è interessante perché è lì che vive la maggior parte del lavoro vero di “architettura”. Le decisioni da piolo-annuale sono rare; ne fai una manciata per sistema, in tutta la sua vita. Le decisioni da piolo-giornaliero sono costanti e non hanno bisogno di cerimonia. Le decisioni da piolo-trimestrale sono quelle dove avere un processo deliberato ripaga, perché capitano abbastanza spesso da accumulare quelle cattive, e sono abbastanza costose da non potertele permettere fatte alla buona.
Cosa copre questo corso, e cosa no
Le ottanta lezioni sono organizzate in dieci moduli. A colpo d’occhio:
- Fondamenta (lezioni da 1 a 8). Definizioni, requisiti, il modello C4, il vocabolario di base. Sei nella lezione 1 in questo momento.
- Una macchina (lezioni da 9 a 16). Cosa puoi fare su una singola VM. Modello dei processi, threading, async, file I/O, l’OS locale come sistema.
- Storage e database (lezioni da 17 a 24). Relazionale contro documenti contro key-value contro colonnare. Indici, transazioni, livelli di isolamento.
- Due macchine e la rete (lezioni da 25 a 32). Latenza, banda, le fallacie del computing distribuito, RPC contro REST contro gRPC, idempotenza.
- Caching, code, e async (lezioni da 33 a 40). Redis, Kafka, message broker, eventual consistency, l’outbox pattern.
- Decomposizione dei servizi (lezioni da 41 a 48). Monoliti contro monoliti modulari contro microservizi. Quando spezzare, quando no, come tracciare le linee di taglio.
- Affidabilità e osservabilità (lezioni da 49 a 56). SLO, error budget, logging, metrics, tracing, on-call.
- Scalare in larghezza (lezioni da 57 a 64). Sharding, partitioning, consistent hashing, leader election, consenso distribuito quanto basta.
- Sistemi multi-region e globali (lezioni da 65 a 72). Active-passive contro active-active, geo-routing, risoluzione dei conflitti, il costo reale di andare globali.
- Pratica e processo decisionale (lezioni da 73 a 80). ADR, fitness function, architettura evolutiva, colloqui, e come capire quando stai over-engineerando.
Diagramma da creare: Un flowchart a 10 box disposto in una griglia 5x2, ogni box etichettato con numero del modulo e cluster di temi. Modulo 1 (in alto a sinistra, Foundations) e Modulo 10 (in basso a destra, Practice) ombreggiati con un colore diverso dagli otto moduli centrali, per marcarli come bookend. Frecce che vanno da sinistra a destra e dall’alto al basso, mostrando l’ordine naturale di lettura. Sotto ogni box, due o tre sotto-temi in testo più piccolo (per esempio sotto “Storage and databases” elencare “relational, document, columnar”). Titolo in alto: “Data and System Architecture: 80 lessons in 10 modules.”
Cosa questo corso non è: un tutorial su uno specifico cloud provider, un’immersione profonda in un linguaggio di programmazione, o un manifesto per un singolo stile architetturale. Menzionerò AWS, Azure, GCP, e i principali progetti open-source lungo la strada, ma l’obiettivo è che tu sia capace di ragionare sui sistemi indipendentemente da quali pulsanti capita che tu stia cliccando quest’anno. I cloud provider rinomineranno i loro prodotti altre tre volte tra quando scrivo questo testo e quando tu lo leggi. I principi no.
Cosa dovresti portarti via da questa lezione
Una frase: l’architettura è l’insieme delle decisioni che è costoso cambiare in seguito.
Due corollari: il volume di discussione non è un segnale affidabile di quanto una decisione sia architetturale, e il costo del ripensamento vive su una scala, non su un binario.
Un’abitudine: quando stai per prendere una decisione tecnica, prenditi cinque secondi per chiederti “se sbaglio su questa cosa, quanto costa disfarla?” Se la risposta è “un pomeriggio”, vai veloce. Se la risposta è “un trimestre”, rallenta e mettila per iscritto. Se la risposta è “un anno”, chiama rinforzi.
La prossima lezione affronta l’altra metà dell’input architetturale: i requisiti. In particolare, la differenza tra requisiti funzionali (cosa fa il sistema) e requisiti non funzionali (quanto bene lo fa), e perché è la seconda categoria a guidare davvero l’architettura. Ci vediamo lì.
Riferimenti
- Bass, Clements, Kazman. Software Architecture in Practice, 4th edition (2021).
- Martin Fowler. Patterns of Enterprise Application Architecture (2002), e i suoi saggi in corso su martinfowler.com.
- Simon Brown. The C4 model, https://c4model.com (consultato 2026-05-01). Trattato nella lezione 3.
- Martin Kleppmann. Designing Data-Intensive Applications (2017).
- Sam Newman. Building Microservices, 2nd edition (2021).
- Beyer, Jones, Petoff, Murphy. Site Reliability Engineering e The Site Reliability Workbook (Google, 2016 e 2018).