Understanding CRDTs - Conflict-free replicated data types explained simply
The problem CRDTs solve
Most people do not think about data conflicts until something breaks.
You update a note on your laptop while your phone is offline. A teammate edits the same document at the same time. A message gets sent from two devices at once. Suddenly the system has to answer a hard question:
Which version of the data is the real one?
In centralized systems, a server hides this problem — it receives updates, imposes an order, and becomes the single source of truth.
In peer-to-peer systems, there is no such referee. Each peer holds its own copy of application state, and those copies need to stay consistent even when updates happen independently. That is exactly where CRDTs come in.
What is a CRDT?
CRDT stands for Conflict-free Replicated Data Type.
The name sounds technical, but it's actually pretty simple:
A CRDT is a data structure that encodes its own merge rules — so replicas can be updated independently and still converge to the same result, without coordination.
The key word is convergence, not conflict elimination. CRDTs do not make conflicts disappear. They make every conflict safe and automatically mergeable by design.
Important distinction: CRDTs do not eliminate conflicts — they encode a deterministic way to merge them. Two users editing the same field still creates a conceptual conflict. The CRDT ensures both edits are handled predictably, without data loss.
With CRDTs in place, peers can:
- Update data locally, without waiting for others
- Go offline and keep working
- Sync later with any number of other peers
- Converge to the same final state, regardless of update order
Why this is hard without CRDTs
Conflicts in peer-to-peer systems happen because:
- Multiple peers can update the same state simultaneously
- Peers can disconnect and reconnect at any time
- Merge operations are order-independent — the same final state results regardless of which updates arrive first
- There is no always-online authority to serialize writes
The naive fix is “last write wins” — whichever update arrives last overwrites everything else. That is simple, but it silently loses data.
The real goal is not to avoid concurrency. It is to make concurrency safe.
A concrete example: the distributed counter
Start with the simplest case: a click counter.
Scenario:
- Alice and Bob both use an app that tracks button clicks
- They go offline independently
- Alice clicks 3 times, Bob clicks 5 times
- Their nodes reconnect and exchange state
Without CRDTs:
- Alice says the counter is 3
- Bob says the counter is 5
- The system picks one — the other's clicks are silently lost
With a CRDT counter (G-Counter):
Note: A G-Counter (Grow-only Counter) is the right fit here — clicks only go up, never down. A PN-Counter extends this to support decrements, but for click counting, a G-Counter is sufficient. Neither is a plain integer: each peer tracks its own contribution separately, and the final value is computed by summing all contributions across peers.
- Alice's node records: Alice=3
- Bob's node records: Bob=5
- After sync, both peers compute: 3 + 5 = 8
No lock, no negotiation, no lost data. The merge is part of the data structure itself.
Note: This example follows the state-based model — peers exchange their full state vectors and merge them locally. An op-based (operation-based) CRDT would instead broadcast individual increment operations, which requires reliable, exactly-once delivery to stay correct. The end result is the same; the delivery requirements differ.
Not all data merges the same way
Counters are the simplest case. Real applications have many different kinds of state, each requiring a different merge strategy.
That is why CRDTs are a family of data structures, not a single mechanism. Here are the main shapes:
Counters
For values that grow through independent contributions.
Use cases: likes, votes, view counts, edit counts
When peers sync, increments from all peers are summed. No contribution is ever lost.
Last-Write-Wins Registers (LWW)
For fields where one final value is correct and the latest edit should win.
Use cases: display name, profile bio, status message
Note: “Last write” is determined by timestamp — either a wall clock or a logical clock. Wall-clock timestamps can be unreliable due to clock skew between devices. This means LWW can silently discard concurrent updates from peers whose clocks are behind. It is simple and predictable, but not safe for high-value fields where every edit matters.
Maps
For structured objects where different fields can be updated independently.
Use cases: a task withtitle, status, assignee, dueDate; a user profile
One peer updates the title, another updates the status — both changes survive because the map merges field by field, not object by object.
Sets
For collections where uniqueness matters.
Use cases: tags, labels, permissions, membership lists
If two peers add different items concurrently, the merged result contains both. No duplicates, no conflicts.
The trickier case is a concurrent add and remove: one peer removes an item while another re-adds it at the same time. Different set CRDTs resolve this differently. An OR-Set (Observed-Remove Set) uses unique tags per addition, so a remove only affects the specific add it observed — a concurrent re-add survives. This add-wins behavior is the most common default for user-facing collections.
Ordered Collections (Sequences)
For ordered content where position matters, not just presence.
Use cases: message feeds, logs, append-heavy streams
Note: Ordering is genuinely hard in distributed systems. Sequence CRDTs (like RGA or LSEQ used in collaborative text editors) require specialized structures to track position without a global coordinator. These are not simple “append and sort” operations — they are designed to deterministically order concurrent insertions without losing any of them.
A practical example: collaborative notes
Picture a shared notes app. Four people are connected to the same context:
- Alice — changes the note title
- Bob — adds two new tags
- Carol — appends a paragraph to the body
- Dan — increments the revision counter
All four actions happen at roughly the same time, with no coordination.
Without CRDT-backed state: one or more edits silently disappear depending on network timing.
With CRDT-backed state:
- The title resolves predictably via LWW — the edit with the latest timestamp wins
- The tags merge as a set — no duplicates, all additions preserved
- The body keeps all valid appended content
- The revision counter sums every peer's contribution
The result is not just “less broken.” It is a fundamentally different user experience — one where the application absorbs concurrency rather than fighting it.
What CRDTs do and do not solve
It is worth being precise about scope.
CRDTs help with:
- Safe merging of concurrent updates
- Guaranteed eventual consistency across replicas
- Local-first, offline-friendly application design
- Eliminating the need for a central coordinator for state convergence
CRDTs do not solve:
- Access control or permissions
- Identity and authentication
- Encryption or privacy enforcement
- Network transport reliability
- Poor application design
Reminder: CRDTs are one approach to distributed state. Alternatives exist — Operational Transformation (OT) is used in Google Docs, and central coordination works well for many products. CRDTs are particularly strong for peer-to-peer systems where coordination is expensive or impossible.
Why CRDTs fit peer-to-peer systems
Peer-to-peer applications are built on a different set of assumptions than traditional web apps:
- Peers are independent actors, not clients of one server
- Offline behavior is expected, not an edge case
- Participation is explicit — not everyone is always reachable
- There is no global consensus layer to serialize every write
In this environment, CRDTs are a natural choice because they require no coordination to update and no coordination to merge. Convergence is guaranteed by the data structure itself — provided that updates are eventually delivered. CRDTs do not fix the transport layer; they guarantee correctness once messages arrive.
That means developers can write application logic that treats local writes as immediate and final, without building custom sync protocols or conflict dialogs on top.
For developers building on Calimero
In Calimero, applications run as WASM modules and interact with CRDT-backed storage. The platform provides CRDT collections so developers do not need to implement merge logic from scratch.
The practical workflow is:
- Define what needs to be shared — map fields, sets, counters, registers
- Choose the right CRDT shape for each piece of state based on its merge semantics
- Write application logic normally — the storage layer handles convergence
The result: applications that work correctly under concurrency without custom synchronization code.
Closing thoughts
The simplest way to understand CRDTs:
They are data structures built for a world where multiple peers can change the same state, without waiting for permission, and still end up consistent.
That is why they matter in peer-to-peer systems. Without them, collaborative local-first applications become fragile. With them, concurrency stops being a source of bugs and starts being a normal part of how the system works.
CRDTs do not make distributed systems easy. They make one specific hard problem — concurrent state convergence — tractable by design rather than by accident.
To learn more, explore the documentation, browse the open-source repositories, or join the conversation on Discord. If you have questions, reach out at support@calimero.network.