Scopri il teorema CAP di Eric Brewer come modello mentale pratico: come consistenza, disponibilità e partizioni influenzano le decisioni nei sistemi distribuiti.

Quando memorizzi gli stessi dati su più macchine, ottieni velocità e tolleranza ai guasti—ma generi anche un nuovo problema: disaccordo. Due server possono ricevere aggiornamenti diversi, i messaggi possono arrivare in ritardo o non arrivare affatto, e gli utenti possono leggere risposte diverse a seconda della replica a cui si collegano. CAP è diventato popolare perché offre agli ingegneri un modo preciso per parlare di quella realtà confusa senza rinunciare ai dettagli.
Eric Brewer, scienziato informatico e cofondatore di Inktomi, introdusse l'idea centrale nel 2000 come una dichiarazione pratica sui sistemi replicati sotto guasto. Si diffuse rapidamente perché rispecchiava ciò che i team stavano già vivendo in produzione: i sistemi distribuiti non falliscono solo spegnendosi; falliscono dividendo.
CAP è più utile quando le cose vanno male—soprattutto quando la rete non si comporta. In una giornata normale, molti sistemi possono apparire abbastanza consistenti e disponibili. La vera prova è quando le macchine non riescono a comunicare in modo affidabile e devi decidere cosa fare con letture e scritture mentre il sistema è diviso.
Questa inquadratura è il motivo per cui CAP è diventato un modello mentale diffuso: non discute sulle migliori pratiche; pone una domanda concreta—cosa siamo disposti a sacrificare durante una partizione?
Alla fine di questo articolo dovresti essere in grado di:
CAP perdura perché trasforma il vago “distribuito è difficile” in una decisione che puoi prendere—e giustificare.
Un sistema distribuito è, in parole semplici, molte macchine che cercano di comportarsi come una sola. Potresti avere diversi server in rack, regioni o zone cloud differenti, ma per l'utente è “l'app” o “il database”.
Per far funzionare quel sistema condiviso su scala reale, solitamente replichiamo: manteniamo più copie degli stessi dati su macchine diverse.
La replicazione è diffusa per tre ragioni pratiche:
Fin qui la replicazione sembra un vantaggio netto. Il problema è che la replicazione crea un nuovo compito: mantenere tutte le copie d'accordo.
Se ogni replica potesse sempre parlare istantaneamente con le altre, potrebbero coordinare gli aggiornamenti e rimanere allineate. Ma le reti reali non sono perfette. I messaggi possono essere ritardati, persi o instradati intorno ai guasti.
Quando la comunicazione è sana, le repliche di solito scambiano aggiornamenti e convergono sullo stesso stato. Ma quando la comunicazione si rompe (anche temporaneamente), puoi ritrovarti con due versioni valide della “verità”.
Per esempio, un utente cambia l'indirizzo di spedizione. La replica A riceve l'aggiornamento, la replica B no. Ora il sistema deve rispondere a una domanda apparentemente semplice: qual è l'indirizzo corrente?
Questa è la differenza tra:
Il pensiero CAP parte esattamente da qui: una volta che esiste la replicazione, il disaccordo sotto guasto di comunicazione non è un caso limite—è il problema progettuale centrale.
CAP è un modello mentale per ciò che gli utenti effettivamente percepiscono quando un sistema è distribuito su più macchine (spesso in luoghi diversi). Non descrive sistemi “buoni” o “cattivi”—solo la tensione che devi gestire.
La consistenza riguarda l'accordo. Se modifichi qualcosa, la lettura successiva (da qualsiasi replica) mostrerà quell'aggiornamento?
Dal punto di vista dell'utente, è la differenza tra “l'ho appena cambiato e tutti vedono lo stesso valore nuovo” e “alcuni vedono ancora il valore vecchio per un po'”.
La disponibilità significa che il sistema risponde alle richieste—letture e scritture—con un risultato di successo. Non necessariamente “il più veloce possibile”, ma “non ti rifiuta il servizio”.
Durante i problemi (server giù, un piccolo problema di rete), un sistema disponibile continua ad accettare richieste, anche se deve rispondere con dati leggermente obsoleti.
Una partizione è quando la rete si divide: le macchine sono in esecuzione, ma i messaggi tra alcune di esse non passano (o arrivano troppo tardi per essere utili). Nei sistemi distribuiti non puoi trattare questo come impossibile—devi definire il comportamento quando succede.
Immagina due negozi che vendono lo stesso prodotto e condividono “1 unità di inventario”. Un cliente compra l'ultimo articolo nel Negozio A, quindi Negozio A scrive inventario = 0. Allo stesso tempo, una partizione di rete impedisce a Negozio B di saperlo.
Se Negozio B rimane disponibile, può vendere un articolo che non ha più (accettando la vendita durante la partizione). Se Negozio B impone consistenza, può rifiutare la vendita finché non può confermare l'inventario più recente (negando il servizio durante la partizione).
Una “partizione” non è solo “internet è giù”. È qualsiasi situazione in cui parti del sistema non possono parlare tra loro in modo affidabile—anche se ciascuna parte è ancora in esecuzione. In un sistema replicato, i nodi scambiano costantemente messaggi: scritture, acknowledgement, heartbeat, elezioni del leader, richieste di lettura. Una partizione si verifica quando quei messaggi smettono di arrivare (o arrivano troppo tardi), creando disaccordo sulla realtà: “La scrittura è avvenuta?” “Chi è il leader?” “Il nodo B è vivo?”
La comunicazione può fallire in modi disordinati e parziali:
Il punto importante: le partizioni sono spesso degradazione, non un guasto netto on/off. Dal punto di vista dell'applicazione, “abbastanza lento” può essere indistinguibile da “non raggiungibile”.
Man mano che aggiungi più macchine, reti, regioni e parti mobili, ci sono semplicemente più opportunità perché la comunicazione si rompa temporaneamente. Anche se i singoli componenti sono affidabili, il sistema complessivo subisce guasti perché ha più dipendenze e più coordinazione cross-node.
Non serve assumere una precisa frequenza di guasti per accettare la realtà: se il tuo sistema gira a lungo e copre abbastanza infrastruttura, le partizioni accadranno.
Tollerare le partizioni significa che il tuo sistema è progettato per continuare a funzionare durante una divisione—anche quando i nodi non possono accordarsi o confermare cosa ha visto l'altra parte. Questo forza una scelta: continuare a servire richieste (rischiando incoerenza) o bloccare/rifiutare alcune richieste (preservando la consistenza).
Una volta che hai replicazione, una partizione è semplicemente una rottura di comunicazione: due parti del sistema non possono parlarsi affidabilmente per un po'. Le repliche sono ancora in esecuzione, gli utenti cliccano ancora i pulsanti e il servizio riceve richieste—ma le repliche non riescono a mettersi d'accordo sulla verità più recente.
Questa è la tensione CAP in una frase: durante una partizione devi scegliere se dare priorità a Consistenza (C) o Disponibilità (A). Non puoi avere entrambe contemporaneamente.
Stai dicendo: “Preferisco essere corretto piuttosto che reattivo.” Quando il sistema non può confermare che una richiesta manterrà tutte le repliche sincronizzate, deve fallire o attendere.
Effetto pratico: alcuni utenti vedranno errori, timeout o messaggi “riprovare”—soprattutto per operazioni che modificano i dati. Questo è comune quando preferisci rifiutare un pagamento piuttosto che rischiare addebiti doppi, o bloccare la prenotazione di un posto piuttosto che sovravendere.
Stai dicendo: “Preferisco rispondere piuttosto che bloccare.” Ogni lato della partizione continuerà ad accettare richieste, anche se non può coordinarsi.
Effetto pratico: gli utenti ottengono risposte di successo, ma i dati che leggono possono essere obsoleti e gli aggiornamenti concorrenti possono confliggere. In seguito fai affidamento sulla riconciliazione (regole di merge, last-write-wins, revisione manuale, ecc.).
Non è sempre un'impostazione globale. Molti prodotti mescolano strategie:
Il momento chiave è decidere—per ogni operazione—cosa è peggio: bloccare un utente ora o risolvere una verità conflittuale in seguito.
Lo slogan “scegli due” è facile da ricordare, ma spesso induce in errore facendo pensare che CAP sia un menu di tre caratteristiche dove puoi tenerne solo due per sempre. CAP riguarda cosa succede quando la rete smette di collaborare: durante una partizione (o qualsiasi cosa che le somigli), un sistema distribuito deve scegliere tra restituire risposte consistenti o rimanere disponibile per ogni richiesta.
Nella realtà dei sistemi distribuiti, le partizioni non sono una opzione che puoi disabilitare. Se il tuo sistema copre macchine, rack, zone o regioni, i messaggi possono essere ritardati, persi, riordinati o instradati in modo strano. Questo è una partizione dal punto di vista del software: i nodi non possono accordarsi su cosa sta succedendo.
Anche se la rete fisica è OK, guasti altrove producono lo stesso effetto—nodi sovraccarichi, pause del GC, neighbor rumorosi, problemi DNS, load balancer instabili. Il risultato è lo stesso: alcune parti del sistema non riescono a parlare tra loro abbastanza bene da coordinarsi.
Le applicazioni non vivono la “partizione” come un evento netto e binario. Vivono picchi di latenza e timeout. Se una richiesta fa timeout dopo 200 ms, non importa se il pacchetto è arrivato al 201 ms o non è mai arrivato: l'app deve decidere cosa fare dopo. Dal punto di vista dell'app, la comunicazione lenta è spesso indistinguibile da quella interrotta.
Molti sistemi reali sono per lo più consistenti o per lo più disponibili, a seconda di configurazione e condizioni operative. Timeout, politiche di retry, dimensione dei quorum e opzioni “read your writes” possono spostare il comportamento.
In condizioni normali un database può sembrare fortemente consistente; sotto stress o problemi cross-region può iniziare a rifiutare richieste (favorendo la consistenza) o a restituire dati più vecchi (favorendo la disponibilità).
CAP serve meno a etichettare prodotti e più a capire il compromesso che stai facendo quando nasce un disaccordo—soprattutto se quel disaccordo è causato dalla semplice lentezza.
Le discussioni su CAP spesso descrivono la consistenza come binaria: o “perfetta” o “qualsiasi cosa”. I sistemi reali offrono un menu di garanzie, ognuna con un'esperienza utente diversa quando le repliche non sono d'accordo o un link di rete si rompe.
Consistenza forte (spesso “linearizzabile”) significa che una volta che una scrittura è confermata, ogni lettura successiva—qualsiasi replica—restituisce quella scrittura.
Il costo: durante una partizione o quando una minoranza di repliche è irraggiungibile, il sistema può ritardare o rifiutare letture/scritture per evitare stati in conflitto. Gli utenti lo notano come timeout, “riprovare” o modalità temporanea sola lettura.
Consistenza eventuale promette che se non arrivano nuovi aggiornamenti, tutte le repliche convergeranno. Non promette che due utenti che leggono nello stesso istante vedranno la stessa cosa.
Cosa possono notare gli utenti: una foto profilo appena aggiornata che “torna indietro”, contatori in ritardo, o un messaggio appena inviato che non è visibile su un altro dispositivo per qualche secondo.
Spesso puoi ottenere una migliore esperienza senza richiedere la consistenza forte completa:
Queste garanzie si mappano bene su come le persone pensano (“non farmi sparire le mie modifiche”) e possono essere più facili da mantenere durante guasti parziali.
Inizia dalle promesse agli utenti, non dalla terminologia tecnica:
La consistenza è una scelta di prodotto: definisci cosa significa “sbagliato” per l'utente, poi scegli la garanzia più debole che impedisce quello sbaglio.
La disponibilità in CAP non è una medaglia da esibire (“cinque nove”)—è una promessa agli utenti su cosa succede quando il sistema non può essere certo.
Quando le repliche non riescono ad accordarsi, spesso scegli tra:
Gli utenti percepiscono questo come “l'app funziona” vs “l'app è corretta”. Nessuna delle due è universalmente migliore; la scelta dipende da cosa significa essere “sbagliati” nel tuo prodotto. Un feed sociale leggermente obsoleto è fastidioso; un saldo conto obsoleto può essere dannoso.
Due comportamenti comuni durante l'incertezza:
Non è una scelta puramente tecnica; è una decisione di policy. Il prodotto deve definire cosa è accettabile mostrale e cosa non lo è mai.
La disponibilità raramente è tutto o niente. Durante una divisione potresti vedere disponibilità parziale: alcune regioni, reti o gruppi di utenti hanno successo mentre altri falliscono. Questo può essere una scelta deliberata (servire dove la replica locale è sana) o accidentale (squilibri di routing, raggiungibilità di quorum non uniforme).
Un compromesso pratico è la modalità degradata: continua a servire azioni sicure limitando quelle rischiose. Ad esempio, permetti la navigazione e la ricerca, ma disabilita temporaneamente “trasferisci fondi”, “cambia password” o altre operazioni in cui la correttezza e l'unicità contano.
CAP diventa concreto quando lo mappi a ciò che gli utenti sperimentano durante una partizione di rete: preferisci che il sistema continui a rispondere, o che si fermi per evitare di accettare dati in conflitto?
Immagina due data center che accettano ordini mentre non possono parlarsi.
Se mantieni il flusso di checkout disponibile, ciascuna parte può vendere l’“ultimo articolo” e finirai per sovravendere. Questo può essere accettabile per beni a basso valore (si gestisce con backorder o scuse), ma doloroso per vendite a disponibilità limitata.
Se scegli consistenza-first, potresti bloccare nuovi ordini quando non puoi confermare lo stock globale. Gli utenti vedranno “riprovare più tardi”, ma eviterai di vendere qualcosa che non puoi evadere.
Il denaro è il classico dominio in cui sbagliare è costoso. Se due repliche accettano prelievi indipendentemente durante una partizione, un conto può diventare negativo.
I sistemi spesso preferiscono consistenza per le scritture critiche: rifiutare o ritardare azioni se non possono confermare il saldo più recente. Scambierai un po' di disponibilità per correttezza, tracciabilità e fiducia.
In chat e feed sociali, gli utenti tollerano piccole incoerenze: un messaggio arriva con qualche secondo di ritardo, un conteggio like è sbagliato, una metrica si aggiorna dopo.
Qui progettare per la disponibilità può essere una scelta sensata, purché sia chiaro cosa è “eventualmente corretto” e si possa unire gli aggiornamenti pulitamente.
La scelta giusta dipende dal costo dell'errore: rimborsi, esposizione legale, perdita di fiducia, o caos operativo. Decidi dove puoi accettare l'obsolescenza temporanea e dove devi fallire chiuso.
Una volta presa una decisione su cosa fare durante una partizione, servono meccanismi che rendano quella decisione effettiva. Questi pattern compaiono in database, sistemi di messaggistica e API—anche se il prodotto non cita mai “CAP”.
Un quorum è semplicemente “la maggior parte delle repliche è d'accordo”. Se hai 5 copie, la maggioranza è 3.
Richiedendo che letture e/o scritture contattino una maggioranza, riduci la probabilità di restituire dati obsoleti o in conflitto. Per esempio, se una scrittura deve essere riconosciuta da 3 repliche, è più difficile che due gruppi isolati accettino verità discordanti.
Il compromesso è velocità e raggiungibilità: se non raggiungi la maggioranza (per partizione o guasti), il sistema può rifiutare l'operazione—preferendo consistenza alla disponibilità.
Molti problemi di “disponibilità” non sono guasti netti ma risposte lente. Impostare un timeout breve rende il sistema reattivo, ma aumenta la probabilità di considerare una risposta lenta come fallita.
I retry possono recuperare da blip transitori, ma retry troppo aggressivi possono sovraccaricare un servizio già in difficoltà. Backoff (aumentare i tempi tra i retry) e jitter (randomizzazione) aiutano a evitare che i retry diventino un picco di traffico.
L'importante è allineare queste impostazioni con la tua promessa: “rispondere sempre” solitamente significa più retry e fallback; “non mentire mai” significa limiti più stretti e messaggi di errore chiari.
Se scegli di restare disponibile durante le partizioni, le repliche possono accettare aggiornamenti diversi e dovrai riconciliarli dopo. Approcci comuni:
I retry possono generare duplicati: addebiti doppi o ordini inviati più volte. L'idempotenza evita questo.
Un pattern comune è la chiave di idempotenza (request ID) inviata con ogni richiesta. Il server memorizza il primo risultato e restituisce lo stesso risultato per ripetizioni—così i retry migliorano la disponibilità senza corrompere i dati.
La maggior parte dei team “sceglie” una posizione CAP su una lavagna—poi scopre in produzione che il sistema si comporta diversamente sotto stress. Validare significa creare intenzionalmente le condizioni in cui i tradeoff CAP diventano visibili e verificare che il sistema reagisca come progettato.
Non serve un vero taglio di cavo per imparare qualcosa. Usa fault injection controllata in staging (e con cautela in produzione) per simulare partizioni:
L'obiettivo è rispondere a domande concrete: le scritture vengono rifiutate o accettate? le letture mostrano dati obsoleti? il sistema si riconcilia automaticamente, e quanto tempo serve?
Se vuoi convalidare questi comportamenti presto (prima di investire settimane a collegare servizi), può aiutare creare un prototipo realistico rapidamente. Per esempio, i team che usano Koder.ai spesso iniziano generando un piccolo servizio (comunemente un backend Go con PostgreSQL e una UI React) e iterano su comportamenti come retry, chiavi di idempotenza e flussi in “modalità degradata” in un ambiente sandbox.
I controlli di uptime tradizionali non catturano il comportamento “disponibile ma sbagliato”. Monitora:
Gli operatori devono avere azioni predefinite quando si verifica una partizione: quando congelare le scritture, quando eseguire failover, quando degradare funzionalità, e come verificare la sicurezza della riconciliazione.
Pianifica anche il comportamento visibile all'utente. Se scegli consistenza, il messaggio potrebbe essere “Non possiamo confermare la modifica adesso—per favore riprova.” Se scegli disponibilità, sii esplicito: “La tua modifica potrebbe impiegare qualche minuto per apparire ovunque.” Una comunicazione chiara riduce il carico sul supporto e preserva la fiducia.
Quando prendi una decisione di sistema, CAP è più utile come veloce audit “cosa si rompe durante una partizione?”—non come dibattito teorico. Usa questa checklist prima di scegliere una funzionalità di database, una strategia di caching o una modalità di replicazione.
Chiediti in ordine:
Se succede una partizione, stai decidendo cosa proteggere per primo.
Evita un'impostazione globale unica come “siamo un sistema AP”. Invece decidi per:
Esempio: durante una partizione potresti bloccare scritture su payments (preferendo consistenza) ma mantenere letture per product_catalog servite da cache.
Scrivi cosa puoi tollerare, con esempi:
Se non riesci a descrivere l'incoerenza con esempi semplici, sarà difficile testarla e spiegare gli incidenti.
Argomenti successivi utili: consenso, modelli di consistenza e SLO e budget di errore.
CAP è un modello mentale per sistemi replicati sotto guasti di comunicazione. È più utile quando la rete è lenta, perde pacchetti o si spezza, perché è in quei momenti che i replica non possono accordarsi e si è costretti a scegliere tra:
Aiuta a trasformare il concetto vago “i sistemi distribuiti sono difficili” in una decisione concreta di prodotto e ingegneria.
Uno scenario CAP vero richiede entrambi:
Se il tuo sistema è su un singolo nodo o non replichi lo stato, i tradeoff CAP non sono il problema centrale.
Una partizione è qualsiasi situazione in cui parti del sistema non possono comunicare in modo affidabile o entro limiti di tempo richiesti—anche se ogni macchina è ancora in esecuzione.
Praticamente, “partizione” spesso si manifesta come:
Dal punto di vista dell’applicazione, “troppo lento” può essere equivalente a “non raggiungibile”.
Consistenza (C) significa che le letture riflettono la scrittura più recente riconosciuta, da qualsiasi replica. Gli utenti lo percepiscono come “l’ho modificato e tutti vedono la modifica”.
Disponibilità (A) significa che ogni richiesta riceve una risposta positiva (non necessariamente il dato più recente). Gli utenti lo percepiscono come “l’app continua a funzionare”, anche se i dati possono essere obsoleti.
Durante una partizione, normalmente non puoi garantire entrambe le cose per tutte le operazioni.
Perché le partizioni non sono opzionali nei sistemi distribuiti che si estendono su più macchine, rack, zone o regioni. Se replichi, devi definire come comportarti quando i nodi non possono coordinarsi.
Quindi “tollerare le partizioni” significa che, quando la comunicazione si rompe, il sistema ha ancora un comportamento definito: o rifiuta/mette in pausa alcune azioni (favorendo la consistenza) o serve risultati alla meglio (favorendo la disponibilità).
Se preferisci consistenza, di solito:
Questo è comune per trasferimenti di denaro, riserve di inventario e cambi di permessi—dove sbagliare è peggio che essere temporaneamente non disponibili.
Se preferisci disponibilità, di solito:
Gli utenti vedranno meno errori netti, ma dati potenzialmente obsoleti, effetti duplicati senza idempotenza o aggiornamenti in conflitto da risolvere dopo.
Sì: puoi scegliere in modo diverso per endpoint o tipo di dato. Strategie comuni includono:
Questo evita un’unica etichetta globale “siamo AP/CP” che raramente rispecchia le esigenze reali del prodotto.
Opzioni utili includono:
Convalida creando condizioni in cui il disaccordo diventa visibile:
Scegli la garanzia più debole che impedisce lo “sbagliato” visibile all’utente che non sei disposto a tollerare.
Prepara anche runbook e messaggi agli utenti coerenti con il comportamento scelto (fail closed vs fail open).