Designing Real-Time Collaborative Editing for a Canva-Style Design Tool
An architecture note for real-time collaborative editing in a Canva-style design tool: requirements, a minimal OT-plus-durable-log design, trust boundaries, operational checks, failure modes, and what would force a redesign.
07 Apr 2026, 13:43 UTC

When several people edit the same design at once — one moving a text box, another recoloring a shape, a third swapping an image — the hard problem is not drawing the pixels. It is keeping every client's document state consistent without forcing editors to wait on each other. This note walks through the requirements, a minimal architecture that satisfies them, where the trust boundaries sit, how to operate the system, and the conditions that would force a redesign. It describes the general architecture a tool like Canva needs for collaborative editing; it is a design exercise based on well-established techniques (operational transformation, CRDTs, durable logs), not a description of any company's internal implementation.
Requirements that shape the design
Four constraints dominate:
- Latency: an edit made by one user should appear on collaborators' screens in roughly under 200 ms round-trip. Anything slower and people start editing stale state and generating avoidable conflicts.
- Convergence: all clients must reach the same final document state even when edits are concurrent — strong eventual consistency, not locking.
- Heterogeneous content: the document mixes text runs, shapes, images, and layout constraints. The conflict model must handle structural edits (move, resize, reorder layers) as well as character-level text edits.
- Access control: viewers, commenters, and editors have different rights, and those rights must be enforced server-side on every operation, not just at connection time.
The smallest design that works
The smallest architecture that meets these requirements has four parts:
- Persistent connections. Each client holds a WebSocket (or equivalent bidirectional channel) to a stateless sync service. Stateless matters: any instance can serve any client, so scaling and failover are simple.
- A durable operation log. Every accepted edit is appended to a durable, ordered stream (Kafka or similar) before being broadcast. The log is the source of truth; everything else is a cache or a projection of it.
- A conflict-resolution engine. Each operation is transformed against concurrent operations before being applied. Operational Transformation (OT) adjusts an operation's parameters — for example, shifting a text insertion offset when another user inserted characters earlier in the same text box — so all replicas apply operations in different orders but converge. CRDTs are an alternative that avoid a central sequencing point, at the cost of more metadata per operation. For a single-writer-per-region sync service, OT with a server-assigned total order is the simpler choice.
- Fast state snapshots. A compacted snapshot of each document lives in a fast store (e.g., Redis) so a client opening a design loads the snapshot and then replays only the log entries newer than that snapshot, rather than replaying the document's entire history.
The flow for one edit: client sends an operation → sync service authenticates the connection, authorizes the operation, validates its schema → assigns it a sequence number and appends it to the log → transforms it against concurrent operations → broadcasts the transformed operation to all subscribed clients → each client applies it locally. The originating client applies its own edit optimistically (immediately, before the server round-trip) and rebases if the server transforms it, which is what keeps typing feeling instant.
Trust and data boundaries
The client is never trusted for validation. Concretely:
- Authentication at connect time: the client presents an OAuth access token when opening the WebSocket; the sync service validates it (signature, expiry, audience) before accepting any operation. Tokens expire, so long-lived connections need a re-authentication or server-side session check rather than trusting the initial handshake forever.
- Authorization per operation: every inbound operation is checked against the caller's role on that document. A viewer's "move layer" operation is rejected, not just ignored by other clients.
- Schema validation: operations are validated against a strict schema (field types, value ranges, referenced element IDs must exist). Malformed operations are rejected and logged — they are a signal of either a buggy client version or abuse.
- Encryption: TLS in transit; encryption at rest for the log and snapshots. The design content itself is user data and should be treated with the same sensitivity as documents in any storage system.
The key boundary: the server decides what happened and in what order. Clients propose operations; the log disposes.
Operational checks
You cannot alert on "collaboration feels wrong," so instrument the mechanics:
- Propagation latency histograms. Measure time from operation accepted at the server to broadcast delivered. Alert on p99 crossing your latency budget, not on averages — averages hide the tail that causes conflicts.
- Conflict/transform rate. Track how many operations required transformation or were rejected. A sudden spike usually means a client release changed operation semantics, not that users suddenly became more contentious.
- Log-to-snapshot consistency jobs. A background job periodically replays the log for sampled documents and compares the result to the stored snapshot. Divergence means a bug in the transform or snapshot logic and should page someone.
- Connection health. Track WebSocket churn (connects/disconnects per second). High churn under load predicts latency degradation before users report it.
- Canary deploys with feature flags. Route a small percentage of documents to new sync-service versions. Because correctness here is subtle, canary on convergence checks, not just error rates.
Failure modes and mitigations
| Failure | Effect | Mitigation |
|---|---|---|
| Network partition / client offline | Client edits diverge from the log | Buffer operations locally; on reconnect, send them for transformation against the missed log entries and reconcile. Show a clear offline indicator so users know edits are pending. |
| Sync service instance crashes | In-flight operations lost from memory | Safe by construction: operations are durable in the log before broadcast. A replacement instance replays from the last acknowledged sequence number. |
| Malformed or malicious operations | Corrupt state or injection attempts | Strict schema validation rejects them before application; log rejections for abuse analysis. |
| Snapshot store outage | New clients cannot load documents quickly | Degrade to read-only mode using the last cached snapshot, or fall back to slower full-log replay for small documents. |
| Token validation misconfiguration | Unauthorized clients could inject operations | Fail closed (reject on any validation error), audit token-validation config changes, and alert on authorization-rejection rate drops to zero — silence can mean the check is broken, not that abuse stopped. |
How to verify it works
Three tests cover the core claims, and none require production traffic:
- Convergence test: run two automated clients editing the same document concurrently — one inserting text, one moving layers. After all operations are acknowledged, fetch the document state from each client and from a fresh log replay; all three must be byte-identical.
- Degraded-network test: use traffic shaping (e.g.,
tc netemon Linux) to add latency and packet loss between one client and the sync service. Confirm edits still converge after the impairment is removed and that no acknowledged operation is lost. - Crash-replay test: kill a sync-service instance mid-edit. After a replacement instance starts, confirm pending operations are replayed from the log and clients receive the correct final state without manual intervention.
What would change the design
This architecture has explicit limits. Connection churn at very large scale can push latency past budget, requiring connection pooling or regional placement of sync instances. Documents with thousands of simultaneous editors (a large classroom, a public template) would strain the single-log-per-document ordering and might need sharding by document region or a CRDT approach that tolerates multiple sequencing points. And if offline-first editing with hours of divergence becomes a hard requirement rather than a reconnect edge case, the rebase-on-reconnect model stops being acceptable and a CRDT design with richer per-operation metadata becomes worth its complexity. Until one of those conditions is true, the log-plus-OT design is the smallest thing that is actually correct.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.