And that's not a bug. It's one of the most elegant algorithms in distributed systems.
Every time you refresh a viral YouTube video and watch the counter tick up, you're looking at a lie.
Not a malicious one. A mathematically precise, deliberately constructed one — built by some of the smartest distributed systems engineers on the planet, for a very good reason.
The actual view count? Nobody knows it exactly. Not YouTube's servers. Not their databases. Not the team that built the counter.
Here's what's actually going on, and why the algorithm behind it — called a G-Counter — is one of the most satisfying ideas in computer science.
The Problem With Counting at Scale
Let's be honest about what YouTube is dealing with.
A viral video gets 50 million plays in its first 24 hours. That's roughly 578 plays every second. These plays are happening from Los Angeles, London, Mumbai, São Paulo — simultaneously, all the time.
YouTube doesn't run one server. It runs thousands, distributed across data centres on multiple continents. Every server is independently accepting "play" events.
The naive solution: have one authoritative counter. Every time someone hits play, that event travels to the central counter, which increments and returns the new value.
The problem: that central counter immediately becomes the bottleneck. At 578 writes per second, you'd need a heavily optimised database just for the counter. And when it goes down — because everything goes down eventually — your entire view counting system goes with it.
The slightly less naive solution: use a distributed lock. Servers coordinate before writing. They take turns.
The problem with locks at scale: latency multiplies, throughput collapses, and network partitions turn the whole thing into a mess. In distributed systems, the CAP theorem tells you this is a fundamental tradeoff — you can't have consistency, availability, and partition tolerance all at once. When you choose strong consistency (everyone agrees on the exact count), you sacrifice availability and performance.
So YouTube made a different choice: stop trying to coordinate at all.
The Insight That Changes Everything
Here's the mental shift that makes G-Counters work.
What does it mean for a view count to be "correct"?
If the US server has recorded 1.2 million views, and the EU server has recorded 800 thousand views, and the Asia server has recorded 600 thousand views — and none of these servers have talked to each other recently — the total isn't ambiguous. It's 2.6 million.
You just add them up.
The reason this is safe is the key constraint: view counts can only go up. Nobody un-watches a video. Nobody subtracts views. The counter is monotonically increasing.
This constraint completely eliminates the problem of conflicting state. If the US server thinks it has 1.2M views and later discovers the EU server also thought the US had 1.1M views (because the EU server's information was slightly stale), there's no conflict to resolve — 1.2M is clearly more up-to-date, because the counter can only increase. You just take the higher number.
That insight — that the maximum value is always the correct value for a grow-only counter — is the foundation of the G-Counter algorithm.
A Simpler Way to See It
Before we look at the code, here's the mental model I find most useful.
Imagine three friends — call them US, EU, and Asia — who are each counting birds in different sections of a massive park. The park is too big to shout across, so they can't coordinate in real time.
Each person has their own notebook. The rule is simple: you can add tally marks, but you can never erase them (because you can't un-see a bird).
At the end of the day, they meet up. US has 3 marks. EU has 5 marks. Asia has 2 marks.
Total birds seen: 10.
Here's what makes this work without any drama: even if they ran into each other in the middle of the day and shared their notebooks, there's no conflict possible. If EU had shared their notebook earlier and it showed 4 marks at the time, and now it shows 5 — 5 is just the more current number. Take the maximum. Move on.
When you replace "friends" with "servers" and "notebook" with "replica state", you have a G-Counter.
The Data Structure
A G-Counter is, at its core, just a map:
type ReplicaId string
type GCounter map[ReplicaId]int64
func Zero() GCounter {
return make(GCounter)
}
Each replica (each server) owns exactly one key in the map. A replica only ever writes to its own key. This invariant is everything — it's what makes the merge operation safe.
Reading the total is simple addition:
func (c GCounter) Value() int64 {
var total int64
for _, v := range c {
total += v
}
return total
}
Incrementing means bumping your own key by 1:
func (c GCounter) Inc(r ReplicaId) GCounter {
newC := make(GCounter, len(c))
for k, v := range c {
newC[k] = v
}
newC[r] = newC[r] + 1
return newC
}
We copy the map to keep things immutable — each increment returns a new GCounter rather than mutating in place. This is a conscious design choice that makes reasoning about state considerably easier.
The Merge Function — Where the Magic Lives
This is the core of the whole algorithm:
func Merge(a, b GCounter) GCounter {
result := make(GCounter)
for k, v := range b {
result[k] = v
}
for k, v := range a {
if val, ok := result[k]; ok {
if v > val {
result[k] = v
}
} else {
result[k] = v
}
}
return result
}
For each replica ID, we take the maximum value seen across both maps.
That's it. No locks. No consensus rounds. No leader election. 15 lines of Go.
Why "Take the Maximum" Is Mathematically Safe
Here's where things get interesting — because this works for a deeper reason than it might seem.
For a merge function to be safe in a system where replicas can sync asynchronously, in any order, any number of times — it needs to satisfy three mathematical properties:
Commutativity:
Merge(A, B)must equalMerge(B, A). It shouldn't matter which server initiates the sync.Associativity:
Merge(Merge(A, B), C)must equalMerge(A, Merge(B, C)). It shouldn't matter how you group the merges.Idempotency:
Merge(A, A)must equalA. If a server accidentally receives the same state twice — because of a network retry, for example — nothing should break.
The maximum function satisfies all three. Maximum(3, 5) equals Maximum(5, 3). Maximum(Maximum(3, 5), 7) equals Maximum(3, Maximum(5, 7)). Maximum(5, 5) equals 5.
A data structure whose merge function has these three properties is called a Convergent Replicated Data Type (CvRDT), or a state-based CRDT. The "convergent" part is the guarantee: no matter what order messages arrive in, no matter how long servers are disconnected, they will always end up agreeing on the same state when they finally sync.
This guarantee — eventual consistency without coordination — is the entire value proposition of CRDTs.
A Concrete Walk-Through
Let's trace through exactly what happens with three servers.
T=0: All servers start at zero.
US: {US: 0}
EU: {EU: 0}
Asia: {Asia: 0}
T=1: US receives 3 plays, EU receives 5, Asia receives 2. All independently.
US: {US: 3}
EU: {EU: 5}
Asia: {Asia: 2}
T=2: US and EU sync. Merge({US:3}, {EU:5}) → {US:3, EU:5}.
US: {US: 3, EU: 5} → Value() = 8
EU: {US: 3, EU: 5} → Value() = 8
Asia: {Asia: 2} → Value() = 2 (still hasn't synced)
T=3: Asia comes back online after a brief network partition. It merges with US.
Merge({US:3, EU:5}, {Asia:2}) → {US:3, EU:5, Asia:2}
All servers: {US:3, EU:5, Asia:2} → Value() = 10
Every play counted. No coordination required during the counting itself. The merge handles everything.
Where This Shows Up in the Real World
G-Counters (or something very close to them) are embedded in more infrastructure than most engineers realise:
YouTube and Twitch view counts — the exact use case we opened with. Regional servers count locally, merge periodically. The displayed count is always approximate, always eventually consistent.
Redis in distributed mode — Redis Enterprise uses CRDT-based counters when data is replicated across data centres. The INCR command on a distributed counter works precisely because of this algorithm.
Analytics pipelines — tools like Segment and Amplitude track event counts at edge nodes before rolling up. The edge nodes are essentially running G-Counters before flushing to a central store.
Riak — the distributed database that helped popularise CRDTs in production, built native G-Counter support into its data model specifically for use cases like these.
Gaming leaderboards — any system tracking "total kills" or "total distance" across game sessions that can happen offline is solving the same problem.
The Trade-Off You Should Know
G-Counters are simple and powerful, but they come with a cost worth understanding.
Because this is a state-based CRDT (also called CvRDT), every time two replicas sync, they exchange their entire state. With 3 replicas, that's a tiny map — 3 key-value pairs. With 1,000 replicas, every gossip round ships 1,000 entries over the wire, even if only one entry changed.
This is the fundamental tension in state-based CRDTs: simplicity of reasoning versus efficiency of replication.
The solution to this is Delta-state CRDTs, where replicas only send the delta — what changed since the last sync — rather than the whole state. Same mathematical guarantees, much lower bandwidth. That's a topic for a future article.
The Constraint Is the Solution
Here's the thing I find most elegant about G-Counters.
The constraint that initially seems limiting — you can only ever increment, never decrement — is exactly what makes the algorithm so clean. It eliminates an entire class of conflict. It makes the merge function trivially provable. It turns a hard distributed systems problem into a single line: take the max.
This pattern shows up everywhere in good system design. When you can't have everything, the question becomes: what do I give up, and what do I get in return? YouTube gave up exact real-time accuracy on view counts. In return, they got a counter that scales to any number of servers, works through network partitions, needs no coordination, and never loses a view.
That's a trade-off most engineering teams would take without hesitation.
What's Next
This is Episode 1 of a series I'm writing on CRDTs — the data structures that power Google Docs, distributed databases, offline-first applications, and collaborative tools.
Episode 2 covers PN-Counters — what happens when you need to both increment and decrement, without conflicts. Spoiler: you just use two G-Counters and subtract. The simplicity is almost frustrating.
The full series:
Episode 1: G-Counter (you're here)
Episode 2: PN-Counter — increment and decrement
Episode 3: G-Set — grow-only sets
Episode 4: LWW Registry — last-write-wins
Episode 5: OR-Set — observed-remove sets
Episode 6: Map CRDT — composing CRDTs into richer structures
Episode 7: The full picture — when to use CRDTs and when not to
Follow along on [your newsletter/Substack URL] to get each episode as it drops.
If you're building distributed systems and want to think through architecture decisions with someone who has been in the weeds on this — I do 1:1 sessions on Topmate.
Top comments (0)