Coinbase: Fragment APIs and Federated Identity at 1.5M Reads/Second
Problem + constraints
Coinbase's Users service is a hard dependency for nearly every critical user journey — login, trading, account management — which made it a single, monolithic service that every other team's request path ran through. Two problems compounded as Coinbase grew:
- Coupling across unrelated domains. Fields like
PhoneNumbersandUserAgreementslived in the same service and the same data store as everything else. A slowdown or bad deploy in one low-traffic fragment could degrade the entire Users service, even for callers who never touched that fragment. - Unpredictable, bursty load. Crypto market volatility drives read traffic that spikes hard and fast — nothing like the smoother diurnal patterns most services are built for. A monolith sized for average load falls over at the peaks that matter most.
- Observability was a nightmare. With every kind of user data flowing through one service and one data store, isolating which fragment was actually responsible for a latency spike or an incident was slow and error-prone.
The constraint wasn't "make it faster" in isolation — it was scaling reads to 1.5M/sec during market surges without losing the consistency guarantees identity data requires (a stale permissions read or a stale account-status read is a correctness bug, not just a UX blemish).
Solution
Coinbase decomposed the monolithic Users service into Fragment APIs: each logical piece of user data (phone numbers, user agreements, account settings, etc.) gets its own independently-served API, backed by its own independent data store. A high-traffic fragment like PhoneNumbers can now scale — and fail — independently of a low-traffic fragment like UserAgreements, instead of them sharing fate through a common service and datastore.
Three pieces make the federation work as a system rather than just a pile of smaller services:
- Federated storage. Each fragment owns its own datastore, so scaling decisions (read replicas, caching, sharding) are made per-fragment based on that fragment's actual traffic shape, not the union of every fragment's traffic.
- Fragment Events. For consumers that need push-based updates rather than polling, Coinbase exposes database CDC (Change Data Capture) as event streams — critically, using the exact same data models as the read APIs. Clients don't have to learn two different shapes of the same data depending on whether they're pulling or subscribing.
- Freshness Tokens — the mechanism that keeps this fast and correct. A Freshness Token is an opaque identifier bound to a specific version of a resource. When a client performs a write, it gets back a token for that write. If it needs to read that data immediately afterward (read-your-writes), it presents the token, and the service guarantees the response is at least as fresh as that version — bypassing cache if the cached copy is older. This means the system can default to aggressively caching reads for the 1.5M/sec common case, without sacrificing correctness for the specific caller that just wrote the data.
Offline analytics is deliberately decoupled from the hot path: fragment data lands in Snowflake with an hourly ingestion lag, so heavy analytical queries never compete with live traffic.
What to steal
- Freshness Tokens are the reusable idea here, independent of Coinbase or crypto. Any system that wants to cache aggressively for the 99% read-heavy case but still guarantee read-your-writes for the caller that just mutated data can use this pattern: return a version token on write, let callers optionally present it on read, and let the read path decide whether to serve from cache or bypass it. It's a cheaper, more targeted tool than "always bypass cache after a write" or "make writes and reads go through the same consistency tier."
- Decompose by data ownership, not by traffic volume. The fragment boundary is drawn around who owns and reasons about a piece of data (phone numbers vs. legal agreements), not around arbitrary load-balancing lines — that's what lets each fragment's team make independent scaling and reliability tradeoffs.
- One data model, two delivery mechanisms. Making Fragment Events emit the same shape as the Fragment APIs is a small design choice that prevents an entire class of "the event stream and the API disagree" bugs and integration friction.
Engineering Lens
This is a textbook example of the tradeoff you'd defend in an architecture review between a shared platform service and per-domain decomposition: the monolithic Users service was simpler to reason about as a single system, but it meant every fragment inherited the blast radius and scaling ceiling of the whole. The Freshness Token detail is the part worth internalizing beyond the decomposition itself — it's a concrete answer to "how do you get aggressive caching and strong-enough consistency in the same system" that doesn't require picking one model globally. In a review, this is the kind of decision you'd frame as "we're trading a small amount of client-side complexity (present a token you already have) for a large win in default cache hit rate" — a good example of pushing a consistency decision to the edge where the caller actually knows whether they need it, rather than centralizing a conservative default that punishes everyone.