L’esercizio del 10x è uno degli esperimenti mentali più utili nel capacity planning. Prendi il sistema come gira oggi, in produzione, con il traffico attuale. Ora immagina che il carico sia dieci volte quello che è. Dieci volte le richieste al secondo, dieci volte il volume giornaliero, dieci volte gli utenti connessi, dieci volte le scritture. Cammina attraverso l’architettura componente per componente e chiediti: cosa si rompe per primo?
L’esercizio ha una proprietà che lo rende più utile di un capacity model preciso. Costringe il team a confrontarsi con i componenti che nessuno sta guardando in questo momento, perché a 1x reggono e nessuno ha motivo di guardarli. A 10x smettono di reggere. L’esercizio è una torcia puntata verso gli angoli dell’architettura dove il team non ha ancora avuto motivo di pensare.
Questa lezione parla della forma di quegli angoli. Quali tipi di componenti scalano con grazia se gli dai più potenza di calcolo; quali sbattono contro un muro; quali pattern architetturali sopravvivono a un salto di un ordine di grandezza e quali pattern devono essere ridisegnati. L’inquadramento viene da una lunga tradizione di scrittura sulla scalabilità, con “Release It!” di Michael Nygard e “Designing Data-Intensive Applications” di Martin Kleppmann come le due fonti più citate in questo angolo del campo.
I pezzi che scalano linearmente
Alcuni componenti sono contenti di ricevere più carico, nel senso che raddoppiare il carico e raddoppiare grosso modo la capacity ti restituisce lo stesso comportamento per richiesta. Sono le parti dell’architettura che sopravvivono al 10x con i soldi piuttosto che con un redesign.
Web tier stateless. Un servizio che non tiene stato per richiesta in memoria tra una richiesta e l’altra può essere scalato orizzontalmente aggiungendo altre istanze identiche dietro a un load balancer. La matematica è approssimativamente lineare: 10x di traffico, 10x di istanze, stessa latency per richiesta. Il qualificatore è “approssimativamente”, perché i load balancer, il DNS, e i costi di stabilire una connessione introducono piccole inefficienze, ma la forma tiene attraverso due ordini di grandezza nella maggior parte dei deployment realistici.
Read replica. Un workload di database read-heavy può essere scalato aggiungendo read replica, fino a un punto che dipende dal database engine e dalla topologia di replication. Postgres gestisce comodamente un numero modesto di streaming replica; setup ben tarati ne fanno girare a dozzine. Il replication lag della lezione 26 diventa un vincolo all’estremità superiore, ma il regime di linear-scaling è reale per i primi 10x di crescita in lettura.
Object storage. S3, GCS e Azure Blob Storage sono progettati per essere effettivamente infinitamente scalabili dal punto di vista del cliente. Il provider assorbe il problema dello scaling. Un team che scrive un terabyte al giorno può scrivere dieci terabyte al giorno senza alcun cambiamento architetturale. Il costo è lineare nel volume; il throughput è, a fini pratici, illimitato.
CDN. Una content delivery network mette in cache i contenuti vicino agli utenti e li serve da edge location che il provider ha già provisionato per la coda lunga dei picchi di traffico. CloudFront, Cloudflare e Fastly pubblicizzano tutti capacity nell’ordine dei terabit al secondo. Un team che mette una CDN davanti a contenuti statici o semi-statici rimuove del tutto la preoccupazione dello scaling da quel layer.
Message queue, nel caso base. Kafka, SQS, Pub/Sub e sistemi simili sono progettati per scalare aggiungendo partition o shard. Producer e consumer possono scalare indipendentemente. Dentro i confini di una topic structure ben progettata, andare da 1x a 10x è un cambio di partition count piuttosto che un redesign.
La forma comune dei componenti linearmente scalabili è la statelessness, lo sharding, oppure “se ne occupa il provider per me”. Sono le parti economiche dell’esercizio del 10x. Le parti costose sono i componenti che hanno uno solo di qualcosa.
I pezzi che sbattono contro un muro
I guai cominciano in qualunque componente che abbia un upper bound rigido, un singolo punto attraverso cui passa tutto il traffico, o un problema di coordinamento che peggiora con la concorrenza.
Database single-leader write-heavy. Postgres, MySQL e la maggior parte degli altri database relazionali concentrano le scritture su un singolo leader di default. Le read replica aiutano con le letture; non aiutano con le scritture. La capacity del leader è quella che la più grande istanza disponibile può sostenere, e quel tetto è finito. Un workload al 70% della write capacity del leader a 1x è al 700% della capacity del leader a 10x, il che vuol dire che non funziona. Le opzioni di redesign sono read-write splitting (che aiuta solo se la frazione di letture cresce), sharding a livello applicativo, o spostare la tabella write-heavy verso un sistema scalabile orizzontalmente (DynamoDB, Cassandra, Spanner). Tutti e tre sono progetti grossi.
Background worker single-thread. Un worker che processa un elemento alla volta su un singolo thread ha una capacity pari al throughput di un singolo CPU core. A 10x di carico, la profondità della coda cresce linearmente, la latency cresce linearmente, e a un certo punto lo SLO si rompe. Il fix sono worker concorrenti, il che espone un nuovo problema: qualunque shared state per worker (lock globali, cache in-memory condivise, generatori di ID sequenziali) diventa il prossimo bottleneck.
Qualunque cosa con lock globali. Un pezzo di codice che prende un lock esclusivo su una riga, una tabella o una coda serializza ogni chiamante concorrente. A 1x con cinque chiamanti concorrenti la contention è impercettibile. A 10x con cinquanta chiamanti concorrenti, il lock è il sistema. Il fix architetturale è rimuovere la contention: lock per chiave invece che lock globali, optimistic concurrency control, code lock-free, contatori sharded. Nessuno di questi è economico da retrofittare.
Query cross-shard. La lezione 31 ha trattato questo direttamente. Le query che devono fare fan-out su più shard hanno un costo che cresce con il numero di shard, e la latency è limitata dal basso dallo shard più lento. Alla scala del 10x il team ha probabilmente aggiunto shard, quindi le query cross-shard sono più lente, e ce ne sono di più. Il risultato è che le query che andavano bene a 1x diventano impraticabili a 10x, e l’applicazione deve essere ridisegnata per evitarle.
Fan-out sincrono. Un request handler che chiama N servizi a valle in serie ha una latency che è la somma di tutte le N latency, e un throughput che cala man mano che il più lento degli N diventa più lento. A 10x, è probabile che tutti gli N siano sotto più carico e più lenti, quindi la richiesta in fan-out diventa più lenta di quanto la somma delle parti farebbe prevedere. Anche il moltiplicatore di throughput è brutale: ogni richiesta esterna che produce N richieste interne significa che la rete interna è a 10 * N volte il suo rate di 1x.
Qualunque cosa con un singolo punto nel critical path. Una singola istanza di cache, una singola lookup DNS, un singolo load balancer, un singolo servizio di auth. A 1x la cosa singola va bene; a 10x la cosa singola è il bottleneck. La risposta architetturale è la replication o lo sharding della cosa singola, che è un redesign piuttosto che un cambio di configurazione.
I classici fallimenti del 10x
Camminando attraverso un’architettura reale sotto la lente del 10x, gli stessi quattro o cinque modi di fallimento saltano fuori dappertutto. Vale la pena riconoscerli per nome perché la diagnosi alle 03:00 è più veloce quando il pattern è familiare.
Esaurimento del connection pool. Ogni istanza applicativa apre un pool di connessioni verso il database. A 1x con dieci istanze applicative e una pool size di cinquanta, il database vede cinquecento connessioni. A 10x con cento istanze applicative e lo stesso pool per istanza, ne vede cinquemila, il che supera il max_connections di default di Postgres ed esaurisce la memoria sul database server. Il fix è il connection pooling lato database (pgbouncer, RDS Proxy) o un tuning aggressivo del pool per istanza, ma la sorpresa viene dall’effetto moltiplicativo del pool moltiplicato per il numero di istanze.
Hot key sulla partition. Un sistema sharded con una chiave come user_id è bilanciato quando il traffico è uniforme tra gli utenti. A 10x di traffico, la varianza si amplifica: l’utente più popolare, il prodotto più popolare, il subreddit più popolare prendono una quota sproporzionata del totale. Lo shard che ospita quella chiave diventa il bottleneck del sistema mentre gli altri stanno fermi. La lezione 28 ha trattato i pattern di rebalancing. Riconosci il sintomo: la CPU di uno shard al 100%, gli altri al 10%, l’utilizzo medio un fuorviante 20%.
L’indice mancante. Una query che scansiona la tabella a 1x con un milione di righe va bene; una query che scansiona la tabella a 10x con dieci milioni di righe ci mette diversi secondi. Peggio, il tempo di query scala in modo super-lineare perché il working set non sta più nel buffer pool, e i seek su disco dominano. Una query che ci metteva 50ms a 1x può metterci 5 secondi a 10x, e il fix è l’indice che avrebbe dovuto esserci dall’inizio. Il pg_stat_statements di Postgres e tool simili fanno emergere i colpevoli.
Amplificazione del fan-out sincrono. Il pattern visto sopra, ma con un particolare modo di fallimento: quando un servizio a valle rallenta, i chiamanti in fan-out aspettano, tengono le loro connessioni, e affamano il tier a monte di worker disponibili. Il tier a monte sembra sano sulla CPU ma incapace di accettare nuove richieste perché tutti i suoi worker sono bloccati sul downstream lento. Il framing dello split-brain della lezione 30 ha un cugino qui: il sistema non è rotto in nessun singolo posto, ma il comportamento cumulativo è rotto ovunque.
Il fallimento del single-flight. Quando una hot key di cache scade, ogni reader concorrente fa miss simultaneamente, ogni reader colpisce il database, e il database crolla. Questo è il problema della cache stampede; la lezione 70 copre le mitigazioni. Riconosci il sintomo: un carico piatto sul database per ore, poi un breve picco che butta giù tutto nello stesso istante in cui i TTL della cache si allineano.
I pattern che sopravvivono
I pattern architetturali che reggono sotto il 10x sono in gran parte quelli che il resto di questo corso ha già introdotto. L’esercizio del 10x è un filtro retrospettivo utile sulle scelte fatte prima.
Servizi stateless con load balancing orizzontale. Il primo principio. Lo stato vive nei database o nelle cache, non nella memoria del servizio. Aggiungere capacity è aggiungere istanze.
Dati sharded con compute sharded. La lezione 29 ha trattato questo; la lezione 32 (Discord) ne ha mostrato la pratica. Un workload diviso su molti shard indipendenti, ciascuno che gestisce una fetta gestibile, scala aggiungendo più shard piuttosto che ingrandendo ciascuno shard.
Comunicazione asincrona, event-driven. Un producer che emette un evento in una topic e se ne va non aspetta i consumer. I consumer processano al loro ritmo, e un consumer lento non può fare backpressure sul producer. Il pattern disaccoppia i modi di fallimento: un rallentamento a valle produce queue lag, non timeout cascade a monte.
Caching ai layer giusti. Il territorio della lezione 70 in dettaglio. CDN al bordo, cache applicative per i dati hot, query cache del database per le letture ripetute. Ogni layer assorbe traffico che il layer successivo dovrebbe altrimenti gestire, e l’effetto moltiplicativo è ciò che rende il 10x sostenibile.
Backpressure e circuit breaker. “Release It!” di Michael Nygard ha definito questi come pattern fondanti. Backpressure: quando un componente a valle è sovraccarico, il monte rallenta o scarica carico deliberatamente piuttosto che ammucchiarne ancora. Circuit breaker: quando un componente a valle sta fallendo, il monte smette di chiamarlo per una finestra di raffreddamento, lascia recuperare il sistema, e riprende con cautela. Senza questi, un rallentamento ovunque diventa un outage dappertutto.
La regola del “ridisegnare a ogni ordine di grandezza”
Un’euristica che è invecchiata bene attraverso decenni di storie di scaling: la maggior parte dei sistemi deve essere ridisegnata a ogni ordine di grandezza nel carico. L’architettura che si adatta a 1000 utenti non si adatta a 10000. L’architettura che si adatta a 10000 non si adatta a 100000. L’architettura che si adatta a un milione non si adatta a dieci milioni.
La ragione è la stessa a ogni passo. A ogni scala, i componenti che erano economici alla scala precedente diventano il bottleneck. Il singolo database che andava bene a mille utenti ha bisogno di read replica a diecimila e di sharding a centomila. Il singolo application server che andava bene a diecimila ha bisogno di scaling orizzontale a centomila e di una CDN a un milione. Le costanti cambiano; le forme cambiano.
Il corollario è che progettare l’architettura del 1000x a 1x è over-engineering. Un sistema che ha bisogno di sharding per il carico che ha effettivamente è fondamentalmente diverso da un sistema che è stato shardato “per sicurezza” per un carico che non vedrà per anni, e il secondo è più difficile da operare senza il payoff corrispondente. Il caso del deployment di Stripe (lezione 56) e il caso del batch di Netflix (lezione 40) fanno entrambi questo punto: scegli l’architettura per il prossimo ordine di grandezza, non per tre ordini in avanti, e rivedila quando attraversi la soglia.
Quello che l’esercizio del 10x ti dà è la vista del passo successivo. Fa emergere cosa si romperebbe a 10x, mette in classifica i bottleneck, e informa i prossimi due trimestri di capacity planning. Farlo una volta all’anno è una cadenza ragionevole; farlo prima di ogni lancio importante è obbligatorio.
La progressione dei bottleneck
Una tipica architettura web-and-data ha una progressione di bottleneck riconoscibile man mano che il carico cresce. La forma si trasferisce attraverso molte architetture pubblicate.
flowchart LR
LB[Load balancer] --> WEB[Web tier]
WEB --> APP[Application tier]
APP --> CACHE[Cache layer]
APP --> DB[(Primary database)]
APP --> EXT[External services]
DB --> REPLICA[(Read replicas)]
classDef linear fill:#d4edda,stroke:#155724
classDef wall fill:#f8d7da,stroke:#721c24
class LB,WEB,APP,CACHE,REPLICA linear
class DB,EXT wall
I nodi verdi scalano linearmente con la potenza di calcolo nel caso tipico. I nodi rossi sbattono contro un muro e hanno bisogno di redesign a ordini di grandezza successivi. La progressione dei bottleneck sotto crescita 10x è di solito:
- Il web tier satura su CPU o memoria; aggiungi più istanze.
- Il connection pool esaurisce il database; aggiungi un connection pooler.
- Il singolo leader del database satura sulle scritture; aggiungi read replica, poi shard o migra.
- La cache diventa un hot spot; aggiungi cache replica o passa a una cache sharded.
- I rate limit dei servizi esterni diventano il fattore limitante; introduci circuit breaker e retry budget.
- Il costo del fan-out domina; ridisegna per chiamate meno numerose e più grosse o per processing asincrono.
Diagramma da creare: un pannello di progressione dei bottleneck che mostra quattro stage della stessa architettura (1x, 10x, 100x, 1000x), con il componente rosso “che ha sbattuto contro il muro” etichettato a ogni stage. Il punto visivo è che il bottleneck si sposta: il database è il bottleneck a 10x, la cache lo diventa a 100x, la replication cross-region lo diventa a 1000x. Ogni redesign sposta il muro più in là.
Eseguire l’esercizio
La meccanica per fare l’esercizio del 10x su un’architettura reale richiede un mezza giornata focalizzata con il platform team, un diagramma di architettura, e le metriche di carico attuali. Il rituale:
- Stampa o disegna l’architettura attuale, con ogni componente annotato con il suo carico attuale (RPS, CPU, memoria, conta delle connessioni, volume giornaliero).
- Per ogni componente, chiedi: qual è la headroom? È al 10% di utilizzo, 50%, 90%? Al 90% si rompe a meno di 10x; al 10% potrebbe sopravvivere al 10x senza alcun cambiamento.
- Per ogni componente sotto il 50% di headroom, chiedi: come scala? Linearmente con le istanze, con un passo manuale di sharding, con un redesign? Qual è il costo operativo di ciascuno?
- Identifica i tre componenti che si rompono per primi. Quelli sono i prossimi due trimestri di lavoro di platform engineering.
L’output è una lista priorizzata di rischi architetturali, ciascuno con un costo grezzo per mitigarlo. La lista è più onesta di qualunque altro artefatto di capacity planning, perché è radicata nei componenti effettivamente in produzione piuttosto che in un modello ipotetico.
Cosa copre la prossima lezione
Questa lezione ha identificato il caching come uno dei pattern di sopravvivenza e ha puntato il dito al problema della cache stampede come classico fallimento da 10x. La lezione 70 si tuffa nel caching nello specifico: i tre tier (CDN, applicazione, database), i quattro pattern canonici di cache, il problema dell’invalidazione, e le mitigazioni dello stampede. La forma del caching cambia quanto aggressivamente il resto dell’architettura debba scalare, e un team che ragiona attentamente sul caching scopre spesso che l’esercizio del 10x è meno scoraggiante di quanto sembrasse all’inizio.
Riferimenti e letture ulteriori
- Michael Nygard, “Release It! Design and Deploy Production-Ready Software”, seconda edizione (Pragmatic Bookshelf, 2018). Il riferimento canonico per i pattern di stabilità: timeout, circuit breaker, bulkhead, backpressure. Ogni capitolo è rilevante per l’esercizio del 10x.
- Martin Kleppmann, “Designing Data-Intensive Applications” (O’Reilly, 2017). Il capitolo 1 inquadra reliability, scalability e maintainability come le tre preoccupazioni; il resto del libro copre ciascuna in profondità. La discussione sulla scalability nel capitolo 1 è una delle più chiare che siano state scritte.
- Pat Helland, “Life Beyond Distributed Transactions” (ACM Queue, 2007 e ristampe). L’argomento che lo scaling costringe il team ad abbandonare astrazioni comode; un vecchio paper che è invecchiato bene.
- Il blog High Scalability,
http://highscalability.com/(consultato 2026-05-01). Una raccolta di lunga data di case study di scaling che documentano le transizioni tra ordini di grandezza in architetture reali. - Stripe Engineering, “Online migrations at scale”,
https://stripe.com/blog/online-migrations(consultato 2026-05-01). Un resoconto pratico del fare il redesign al confine del 10x mentre il traffico live scorre.