Benvenuto al Modulo 2. Il Modulo 1 parlava dell’architettura che costruisci prima di doverti preoccupare dei sistemi distribuiti: una macchina, un processo, un database, un loop veloce, e la disciplina di tenerlo così finché il carico te lo permette. Stripe, Shopify e Basecamp sono comparsi nella lezione 8 come prova che “finché il carico te lo permette” è di solito più a lungo di quanto la gente pensi.
Ma prima o poi, sui sistemi più ambiziosi, la singola macchina smette di bastare. Ne aggiungi una seconda. E nel momento esatto in cui lo fai, hai un sistema distribuito, con tutti i nuovi failure mode che si porta dietro. Tutto il resto di questo corso è, in un certo senso, una resa dei conti con quel singolo salto da una macchina a due.
Prima di arrivare a pattern specifici, dobbiamo affrontare le bugie. I sistemi distribuiti sono difficili non perché gli algoritmi siano esotici, ma perché i principianti (e un numero preoccupante di persone che dovrebbero saperne di più) ragionano sulla rete come se fosse una function call. Non lo è. Le differenze sono grandi, costose, e la fonte di praticamente ogni incidente di produzione nella storia del software distribuito.
L’enumerazione più utile di queste bugie è una lista di otto affermazioni scritta da Peter Deutsch alla Sun Microsystems nel 1994, con altre due aggiunte da James Gosling nel 1997. La lista si chiama le otto fallacie del computing distribuito. Ognuna di esse è qualcosa che suona vera la prima volta che la senti, è di fatto falsa, e ti farà spedire codice che si rompe in modi sorprendenti nel momento in cui il tuo ambiente diventa ostile.
La lista, in ordine canonico:
- The network is reliable (la rete è affidabile).
- Latency is zero (la latenza è zero).
- Bandwidth is infinite (la banda è infinita).
- The network is secure (la rete è sicura).
- Topology doesn’t change (la topologia non cambia).
- There is one administrator (c’è un solo amministratore).
- Transport cost is zero (il costo di trasporto è zero).
- The network is homogeneous (la rete è omogenea).
Le passeremo in rassegna una per una guardando il failure mode che ciascuna produce. La tesi di questa lezione è semplice: i bug nei sistemi distribuiti vengono dal credere a una di queste. Ogni pattern architetturale nel resto del corso esiste, in parte, per rendere il fallimento recuperabile invece che catastrofico.
1. The network is reliable
Non lo è. Le reti perdono pacchetti, i router si riavviano, i link sfarfallano, le fibre si tagliano, le misconfigurazioni BGP buttano in black-hole intere region, i cloud provider hanno outage all’incirca una volta a trimestre, e il load balancer davanti al tuo servizio si pianta nel momento esatto in cui l’on-call engineer è uscito a cena. Una qualsiasi di queste cose trasforma una “chiamata sincrona a un altro servizio” in “una richiesta che potrebbe essere passata, potrebbe non essere passata, oppure potrebbe essere passata due volte.”
Perché questa fallacia è seducente? Perché in sviluppo chiami gli altri servizi su localhost, dove l’interfaccia di loopback davvero è affidabile. La prima volta che il tuo codice gira in un ambiente reale con una rete reale tra due macchine reali, l’assunzione si rompe.
Il failure mode concreto: la tua richiesta POST /charge va in timeout. L’addebito è andato a buon fine? Non lo sai. La logica di retry parte e rimanda la richiesta. Se l’originale era andata a buon fine, ora hai addebitato il cliente due volte. Se non hai scritto idempotency key nel tuo protocollo, hai un bug finanziario che nessuno può sbrogliare senza intervento manuale.
La risposta architetturale è un piccolo set di pattern a cui dedicheremo lezioni successive: idempotency key, retry con exponential backoff e jitter, timeout a ogni hop, circuit breaker, e l’assunzione che ogni singola network call possa dover essere replayata senza danni.
2. Latency is zero
Non lo è. Un round trip su una LAN veloce è dell’ordine di mezzo millisecondo. Tra availability zone dentro una region cloud, uno o due millisecondi. Tra region, da venti a duecento. Tra continenti, da cento a trecento. La luce ha un limite di velocità e ce l’hai anche tu.
La fallacia è seducente perché le chiamate di funzione in-process sono di fatto gratuite a questa scala. Il tuo IDE mostra la stessa sintassi per una local method call e una remota (specie nei sistemi che generano stub come gRPC), quindi è facile dimenticarsi che una costa nanosecondi e l’altra dieci milioni di volte tanto.
Il failure mode classico è la chatty API. Il tuo endpoint “list orders” restituisce una lista di order ID. Il frontend poi fa una chiamata per ID per ottenere i dettagli dell’ordine. Su una singola macchina, va bene. Su una rete con round-trip time di venti millisecondi, recuperare cento ordini prende due secondi prima che qualunque dato dell’ordine sia arrivato. Questo è il problema N+1 che potresti aver incontrato nei database, e diventa drammaticamente peggio sul filo.
La risposta architetturale è progettare API che facciano batching, paginazione, e restituiscano abbastanza dati per richiesta da tenere basso il numero di round trip. Questa regola tornerà quando parleremo di API design, gRPC contro REST, e della differenza tra protocolli request-response e di streaming.
3. Bandwidth is infinite
Non lo è. Un link da 10 gigabit al secondo sembra tanto finché non provi a copiare uno snapshot da cento gigabyte attraverso quello durante l’orario di lavoro, momento in cui le richieste di tutti gli altri rallentano. Le egress fee del tuo cloud provider sembrano triviali finché non spedisci una feature che copia grossi blob cross-region e scopri a fine mese di aver speso più in banda che in compute.
Questa fallacia è seducente perché in sviluppo i dati sono piccoli, il link è locale, e la banda sembra gratuita. In produzione i dati sono grandi, il link è condiviso, e la bolletta è mensile.
I failure mode sono caricamenti di pagina lenti per over-fetching, link congestionati durante i backup, e addebiti egress a sorpresa. Dedicheremo una lezione successiva alla data locality (tieni il compute vicino ai dati, non viceversa) e ai pattern per spostare grandi data set senza saturare il production path.
4. The network is secure
Non lo è. Il traffico non cifrato tra servizi nello stesso data centre è stato il default per anni, sulla teoria che il perimetro del data centre fosse il confine di sicurezza. Poi qualcuno è entrato nel perimetro (ogni anno, più volte, in aziende che dovrebbero saperne di più) e ha scoperto che il centro morbido non aveva autenticazione sulle RPC interne.
La fallacia è seducente perché scrivere configurazione TLS è fastidioso e l’interno del tuo VPC sembra un posto privato. Non è privato. Contiene il tuo codice, le tue dipendenze, il codice dei tuoi colleghi, a volte un agent di terze parti che è stato compromesso a monte, e traffico da un migliaio di macchine che non conosci personalmente.
La best practice attuale è il zero-trust networking: ogni servizio autentica ogni altro servizio con mutual TLS, ogni chiamata porta un token a vita breve, e “dentro al perimetro” fornisce esattamente zero fiducia implicita. Questo è ciò che strumenti come SPIFFE, Istio e Linkerd esistono per rendere trattabile. Ci torneremo nelle lezioni su sicurezza e operability.
5. Topology doesn’t change
Cambia, eccome. I servizi salgono, scendono, vengono rideployati, vengono rischedulati da Kubernetes su un altro nodo, cambiano IP, cambiano porta, perdono il loro record DNS per trenta secondi durante un rollout, vengono scalati da tre a dieci istanze e indietro, vengono deployati in blue-green con due set paralleli di pod. La topologia che hai schizzato sulla lavagna è uno snapshot di un momento.
La fallacia è seducente perché in sviluppo l’indirizzo del database è in un file di config e non cambia mai. In produzione l’indirizzo è “dovunque l’orchestratore lo abbia messo questo minuto” e il file di config è una bugia stantia.
Il failure mode sono indirizzi IP hardcoded, DNS TTL che mentono, connessioni a istanze che non esistono più, e l’intera categoria di bug in cui un client cacha un endpoint di servizio e continua a provare a raggiungerlo molto dopo che si è spostato. La risposta architetturale è la service discovery (Consul, etcd, Kubernetes services), il connection pooling che si rinfresca, e la disciplina di trattare ogni indirizzo come effimero.
6. There is one administrator
Non c’è. Il tuo sistema gira su infrastruttura gestita dal tuo platform team, dal tuo cloud provider, dal tuo DNS provider, dalla tua CDN, dalla tua TLS certificate authority, dal tuo vendor di observability, e probabilmente da un paio di API di terze parti. Ognuno di quelli è un’organizzazione separata con priorità separate, finestre di manutenzione separate, e idee separate su cosa significhi “graceful degradation”.
La fallacia è seducente in una startup di cinque persone dove l’unico ingegnere che sa tutto è nella stanza. Smette di esserlo la prima volta che AWS ha un outage in us-east-1 e tu scopri che il tuo servizio “interno” dipende da una feature solo-us-east-1 di un SaaS di terze parti che ti eri dimenticato di star usando.
La conseguenza architetturale è il dependency mapping, la vendor risk analysis, il principio di progettare per graceful degradation quando un upstream è giù, e la consapevolezza che l’affidabilità del tuo sistema è il prodotto dell’affidabilità di ogni componente, non solo delle parti che hai scritto tu.
7. Transport cost is zero
Non lo è. Il traffico cross-AZ in AWS costa circa un centesimo per gigabyte in ciascuna direzione, al 2026. Cross-region è molte volte tanto. Le tariffe del NAT Gateway aggiungono un’altra fee per gigabyte processato. L’egress verso internet è il più caro di tutti. Un’architettura di servizi chatty che attraversa un confine di AZ a ogni richiesta può accumulare una bolletta che fa impallidire il costo del compute.
Questa fallacia è seducente perché la rete non compare nella tua bolletta oraria di compute. Compare in una linea separata che nessuno guarda finché il CFO non ne chiede conto.
Il failure mode è la bolletta a sorpresa. Ci torneremo quando copriremo i pattern multi-AZ e multi-region e il trade-off tra availability (più zone) e costo (ogni attraversamento di zona ha un prezzo).
8. The network is homogeneous
Non lo è. Il tuo traffico attraversa macchine Linux, macchine Windows, macchine ARM e macchine x86. Attraversa router di tre vendor diversi. Colpisce load balancer che interpretano HTTP in modo leggermente diverso l’uno dall’altro. La libreria TLS sul client si comporta in modo leggermente diverso dalla libreria TLS sul server. Alcuni hop supportano HTTP/3, altri solo HTTP/1.1. Il tuo payload gRPC va bene finché non attraversa un proxy che bufferizza in un modo che il client non si aspettava.
La fallacia è seducente perché in sviluppo è tutto la stessa Docker image. In produzione non è tutto uguale.
Il failure mode è il bug di protocol-mismatch, che è uno dei tipi più demoralizzanti: il sistema funziona, in gran parte, finché esattamente una combinazione di versione del client e versione del proxy interagisce male, e il risultato è un tasso di errore sporadico del cinque percento che nessuno riesce a riprodurre in staging. La risposta architetturale è la disciplina di interoperabilità: attieniti a protocolli standard, versiona le tue API, testa contro i proxy reali dietro i quali girerai, e assumi che “omogeneo” sia una finzione da ambiente di sviluppo.
Cosa può andare storto in una sola network call
Otto fallacie sono tante da tenere in testa, quindi aiuta vederle come otto modi in cui una singola richiesta può fallire.
flowchart TD
A[Client makes one network call] --> B{What can go wrong?}
B --> F1[Packet lost or duplicated]
B --> F2[Round trip slower than expected]
B --> F3[Payload too big for the link]
B --> F4[Traffic intercepted or replayed]
B --> F5[Destination has moved]
B --> F6[Upstream dependency unavailable]
B --> F7[Crosses a paid boundary]
B --> F8[Proxy or library mismatch]
F1 --> R[Architectural responses]
F2 --> R
F3 --> R
F4 --> R
F5 --> R
F6 --> R
F7 --> R
F8 --> R
R --> P1[Idempotency, retries, timeouts]
R --> P2[Batching, locality, caching]
R --> P3[mTLS, zero trust, tokens]
R --> P4[Service discovery, healthchecks]
R --> P5[Cost-aware topology]
R --> P6[Versioning, contract tests]
Ogni pattern architetturale nel resto di questo corso atterrerà da qualche parte sul lato destro di quel diagramma. I circuit breaker sono una risposta alla fallacia 1. Gli strati di caching sono una risposta alle fallacie 2 e 3. Le service mesh sono una risposta alle fallacie 4, 5 e 8. Il design multi-region è una scommessa calibrata contro la fallacia 6 (amministratori diversi, blast radius diverse) e una gestione attenta della fallacia 7 (costo). Quando leggi di un pattern e ti chiedi perché qualcuno si sia disturbato, la risposta è quasi sempre: perché qualcuno ha creduto a una delle otto fallacie e si è scottato.
Come usare la lista
Non hai bisogno di memorizzare la lista in quanto tale. Hai bisogno di interiorizzare l’abitudine di chiederti, prima di scrivere qualunque codice che attraversa un process boundary: quale di queste sto per assumere?
Un esercizio utile, specialmente nelle design review, è prendere il call graph proposto e percorrerlo chiamata per chiamata. Per ognuna: come si comporta quando la risposta è lenta? Quando la risposta non arriva mai? Quando la risposta arriva due volte? Quando la destinazione si è spostata? Quando il link è saturo? Quando un proxy nel mezzo ha un bug? Quando arriva la bolletta? Se la risposta a una qualsiasi di queste è “non ci abbiamo pensato”, quella chiamata è un incidente futuro.
Gran parte del resto del corso è dedicata a rispondere a queste domande come si deve: con timeout, idempotenza, observability, service discovery, traffic shaping, contract testing, e la disciplina noiosa ma essenziale di trattare la rete come un mezzo ostile, lossy e probabilistico, invece che come una function call affidabile.
Cosa viene dopo
Le fallacie ti dicono cosa è difficile. Non ti dicono cosa farci. Le prossime due lezioni introducono il trade-off centrale che organizza il resto della teoria dei sistemi distribuiti.
La lezione 10 è il teorema CAP nella pratica: in presenza di una network partition (la fallacia 1, portata al suo estremo), devi scegliere tra consistency e availability per ogni operazione. Ripercorreremo cosa dice davvero CAP, le misletture più comuni, e le scelte reali che sistemi reali (DNS, ledger bancari, Cassandra) fanno.
La lezione 11 estende questo a PACELC, che aggiunge il caso quotidiano su cui CAP tace: anche quando non c’è una partition in corso, stai barattando latenza per consistency su ogni write. Una volta che vedi il quadrante PACELC, lo spazio di design dei database distribuiti smette di sembrare uno zoo e inizia a sembrare un set di scelte esplicite e nominate.
Siamo ora saldamente nel territorio in cui le lezioni del Modulo 1 (parti semplice, rimanda la complessità) incontrano la realtà che alcuni sistemi devono davvero scalare oltre una singola macchina. Conoscere le fallacie è ciò che ti permette di architettare la seconda macchina senza pentirtene immediatamente.
Citazioni e letture di approfondimento
- Peter Deutsch, “The Eight Fallacies of Distributed Computing” (1994), riassunto su
https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing(consultato 2026-05-01). Il memo originale di Sun Microsystems è riprodotto in diverse collezioni archiviate; l’articolo di Wikipedia linka i PDF canonici. - Arnon Rotem-Gal-Oz, “Fallacies of Distributed Computing Explained” (2006), l’espansione più citata della lista con esempi concreti (consultato 2026-05-01).
- Le pagine di pricing AWS per traffico cross-AZ, NAT Gateway e inter-region, per i numeri usati nella fallacia 7 (consultato 2026-05-01).
- Per la fallacia sulla sicurezza: i paper BeyondCorp di Google (dal 2014 in poi) e la specifica SPIFFE (
https://spiffe.io/) per la forma moderna del zero-trust networking dentro i data centre.