A replicated write path that forces every transaction to line up behind its slowest healthy node will eventually cap the whole platform, and the durable fix is usually a change in coordination scope rather than a hardware purchase. That is the transferable engineering result coming out of a storage migration measured in months, not quarters. Over the past year, Git traffic expanded sharply and 99.99999% of Git repositories came to live in two places at once: GitHub data centers and Azure. The instructive part is the residue — 0.00001% of repositories, the busiest on the platform, all of them stopped by one physical constraint: a 25ms round trip.
Below, the migration is decomposed the way a data platform team would evaluate it: the sequencing decision, the two architectural changes, the measured throughput effect, the control-plane work that has to exist alongside them, and the failure modes to check before you schedule a cutover.
The shape and the timing of the migration were dictated by the agentic shift: fast traffic growth, a hard ceiling on power capacity in the data centers and the nearby region, and physical supply chain constraints. At the start, remaining capacity plus every available expansion left enough room to migrate safely — and not nearly enough room to move slowly.
New Git scaling designs on cloud infrastructure had already been prototyped, so a combined “new architecture on the new platform” cutover was technically conceivable. It was rejected on risk grounds: swapping storage architecture and hosting platform together during rapid growth carried too much risk. The work was ordered instead — move data first, then evolve the architecture after arrival.
That ordering is a dependency argument rather than a stylistic preference. Git operations are a Tier 0 service: they are core GitHub functions, and nearly everything depends on repository data. Data, services, and dependencies therefore had to go first, because nothing that sits on top of a moving state store can be safely re-architected.
The move was framed by two principles in an earlier post: minimize coordination, and decouple storage from compute. The bridge between the old design and the new one is a single change in how Spokes serializes writes. That change pulls the busiest repositories into Azure, and it also opens a path to replicas without persistent state.
Under the current architecture, a repository hosted in more than one region has a lower ceiling for write throughput. The Spokes quorum only needs a majority to commit a write, yet it waits for every healthy replica to answer, up to a timeout, before continuing. Slow replicas therefore delay every write instead of merely falling behind.
The compounding factor is lock scope. Because each write holds a repository-wide lock until commit, the slowest healthy replica also fixes the throughput ceiling. Once a replica sits in the target Azure region, it is about 1,000 miles from the older data centers, so the cross-region delay is a 25ms round trip, and a write needs multiple trips. A repository already near its ceiling cannot absorb that penalty on every serialized write; it would fall further behind with every push.
Read this as a queueing problem, not a bandwidth problem: the latency does not just add time to one request, it serializes the whole write path behind the slowest participant.
Minimizing coordination means not serializing what does not need to be serialized. Git consistency rules attach to each reference separately: after the server accepts a reference update, no client should later see an older value, only that value or a newer one. The rule applies whether the update is a fast-forward, a force push, or a delete. In most cases, different references can be updated independently.
So the quorum scope shifts from the entire repository to individual references.
| Serialization scope | Queue behavior | Write overlap | Effect on the throughput ceiling |
|---|---|---|---|
| Repository-wide | one write in flight at a time | none | set by the slowest healthy replica, multiplied by cross-region round trips |
| Per-reference | only shared references wait | different references overlap | rises in proportion to the number of references updated concurrently |
Holding a repository-wide lock through an update, including round trips, makes the quorum behave like one unordered request queue with one write in flight at a time; each queued write lasts as long as the slowest healthy replica. As the write rate rises, the queue lengthens and each write waits longer than the previous one.
Per-reference serialization narrows waiting to writes that contend on the same reference. In practice, most repository writes touch different references — agents and developers pushing their own branches — so they no longer block one another. A cross-region latency cost remains for each write, but different references can move at the same time, removing the single repository-wide bottleneck.
The measured effect matters for capacity planning: in internal testing with realistic traffic, writes to different references consistently saw 3–4x higher throughput at lower latency. Those adjustments lifted the write throughput ceiling in proportion to concurrent references and moved the last remaining repositories into Azure.
Workload skew drives this one. Most repositories are heavily read-skewed, with somewhere between 100 and 1,000 reads for every write. Today, reads and writes use the same replicas, so they compete for the same resources and cannot be scaled independently. Reads could benefit from far more in-memory cache, but that memory is also needed for compaction. Once the roles are split, each can have its own configuration.
Decoupling storage from compute is what makes the split legal: once repository data is durably stored in Azure Blob Storage and updated with each incoming write, read traffic can be separated from the write quorum, and the compute for each can be scaled independently.
The initial move makes object storage a quorum member through a virtual replica. The compute standing for it is the first fully ephemeral node: it bridges to object storage, streams incoming writes, and serves no reads. Because it is part of the quorum, a write is not acknowledged until the data is durable in object storage.
That adds a little write latency. As with cross-region round trips, per-reference serialization prevents the latency from becoming a throughput bottleneck: the virtual write replica coalesces transactions on disjoint references into one set of network I/Os to object storage, so a busy repository pays for one round trip across many concurrent writes instead of one per write. For now, the virtual write replica also performs compaction, and read replicas inherit the compacted state.
With that replica in place, many virtual read replicas can be scaled out as a repository needs them. Each read replica warms its cache from Azure Blob Storage and starts taking read traffic without joining the quorum, using object storage as the source of truth and reconciling its local state against it in the background. In internal testing, the distributed load testing harness routinely found ceilings in the current architecture; against virtual read replicas, the harness hit its own limits first.
Two caveats belong in any rollout plan built on this pattern. Background reconciliation suits some read workloads but not all; serving every kind of read requires moving that reconciliation in-line. And although per-reference serialization has increased write throughput incrementally, the quorum remains the bounding factor for further growth.
Today GitHub rests on the Spokes quorum. Over the years, Spokes has become much more than a coordinator: an intelligent routing and load balancing layer; a unified API used by internal clients to access and transact repository data at well over 100 billion operations per day; and background systems called “sweepers” that handle repository placement, repair, and rebalancing between storage nodes. Most of the work under the Spokes umbrella also fits the new architecture.
The exception is the commit protocol itself, and it is the part platform owners should plan around rather than assume away: the commit protocol that carried GitHub to a billion repositories will not carry it to the next billion and beyond.
Because they are the busiest repositories on the platform, and they were already near the write-throughput ceiling. A ceiling that is already saturated cannot absorb an added cross-region penalty on every serialized write — each push would leave the repository further behind.
No. A cross-region latency cost remains for each write. What disappears is the repository-wide bottleneck: writes that touch different references proceed at the same time, so the ceiling lifts in proportion to the number of references updated concurrently.
Placement, repair, and rebalancing between storage nodes have to run in the background, and the redesign forced a rethink of where durable reference state lives and how compaction and garbage collection scale.
Yes — that was the operational argument for the ordering. Agent traffic is not slowing, and each step gave repositories more headroom as it landed, without waiting for the end state.
Three things carry over to any pipeline or state-store relocation: order the work by dependency rather than by architectural elegance; treat lock and quorum scope as a first-class scaling knob, measured against your slowest healthy node; and store durable state once, then scale read compute independently instead of buying permanent replicas. The remaining challenge is ordering the small mutable part of a write as fast as possible without relocating the bottleneck — designing that on a whiteboard is one thing, doing it underneath a billion repositories that developers and agents write to around the clock is another.
That end state, what it does for write throughput, the second-order problems that had to be solved to reach it, and why a small consensus group remains somewhere in the stack, is due next week — at GitHub Universe and in the final post in this series. Brian Celenza contributed to this post.
By David Nakamura for AI & Data News (AIdataNewsGlobal), DataEngineering desk. Editorial contact: editorial@aidatanews.com.