For a Figma-like collaborative editor at the scale of millions of users, the architecture is less about supporting millions of simultaneous collaborators on one document (which almost never happens) and more about efficiently serving millions of users across millions of documents, with thousands to tens of thousands of active collaboration sessions.
A common architecture looks something like this:
CDN
│
┌────────────┴────────────┐
│ │
API Gateway WebSocket Gateway
│ │
├──────────────┐ │
│ │ │
Auth Service File Metadata │
│ │ │
└──────────────┼──────────┘
│
Collaboration Router
│
┌─────────────┴─────────────┐
│ │
Collab Worker A Collab Worker B
(owns Doc X) (owns Doc Y)
│ │
├────────────┐ │
│ │ │
Redis Cache Event Log (Kafka/Pulsar)
│ │
│ Persistence Workers
│ │
└────────────┴─────────────┐
│
Object Storage
Snapshot Database
Core principles
1. Documents are the unit of scaling
Don't think in terms of users.
Think in terms of documents.
Each open document is assigned to exactly one collaboration worker.
Document A → Worker 17
Users:
Alice
Bob
Carol
Dave
Every edit for that document goes through Worker 17.
This avoids distributed locking.
2. Stateless frontend servers
Your API servers should remain stateless.
Responsibilities:
- authentication
- permissions
- metadata
- billing
- file listing
- REST APIs
Realtime editing happens elsewhere.
3. Dedicated collaboration servers
These are the heart of the system.
Responsibilities:
- maintain WebSocket connections
- keep document in memory
- receive operations
- validate
- order operations
- broadcast updates
- periodically checkpoint
One server may hold:
- 2,000 small docs
- 200 medium docs
- 20 huge docs
depending on memory.
4. Sticky routing
Users editing the same file must reach the same collaboration worker.
Typical flow:
Open file
↓
Metadata service
↓
"Document lives on Worker 82"
↓
Connect websocket
↓
Worker 82
Worker ownership is tracked through a coordinator.
5. CRDT or Operational Transform
Modern systems usually favor CRDTs.
Each operation looks like:
MoveNode
NodeID: 129
dx: 13
dy: -4
Timestamp: ...
Actor: User123
instead of sending:
Entire document
Bandwidth stays tiny.
6. Event sourcing
Never overwrite the document on every edit.
Store operations.
Doc
Snapshot
+
Edit 1
Edit 2
Edit 3
...
Edit 1500
Eventually create another snapshot.
Snapshot
+
Last 200 edits
This provides:
- undo
- history
- audit
- replay
- crash recovery
7. Snapshot storage
Large documents are expensive to replay from zero.
Keep periodic snapshots.
Example:
Snapshot every 500 edits
Snapshot
+
501
502
503
...
Opening a document means:
Load snapshot
↓
Replay recent edits
↓
Ready
8. Pub/Sub only where needed
Suppose Alice edits.
Alice
↓
Worker
↓
Broadcast
↓
Bob
Carol
Dave
No database lookup.
No Kafka.
No Redis.
Just memory.
Kafka is mainly for persistence and analytics, not intra-document fan-out.
9. Persistence pipeline
Worker
↓
Append edit
↓
Kafka
↓
Storage worker
↓
Database/Object Store
The collaboration worker shouldn't block on disk writes.
10. Presence service
Separate presence from editing.
Presence includes:
- cursor
- selection
- viewport
- typing
- online status
These updates are ephemeral.
They don't need permanent storage.
Cursor moved
↓
Worker
↓
Broadcast
↓
Discard
11. File storage
Separate binary assets.
Document
├── vectors
├── text
├── constraints
└── image references
Images
↓
S3/GCS/etc.
The document stores references, not image bytes.
12. Databases
Different data belongs in different systems.
| Data | Storage |
|---|
| Users | SQL |
| Teams | SQL |
| Billing | SQL |
| Permissions | SQL |
| File metadata | SQL |
| Snapshots | Object storage / NoSQL |
| Edit log | Kafka + object storage |
| Search | Elasticsearch/OpenSearch |
| Presence | Memory/Redis |
13. Scaling example
Suppose:
- 5 million registered users
- 150k online
- 35k active documents
- average 4 collaborators/document
Then:
150,000 websocket connections
↓
400 collaboration workers
↓
~375 sockets/worker
This is quite manageable with modern servers. The challenge is balancing memory, CPU, and document size rather than raw connection count.
14. Failure recovery
If a collaboration worker dies:
Worker crashes
↓
Coordinator detects failure
↓
Another worker loads:
Snapshot
+
Recent edits
↓
Reconnect users
↓
Continue editing
Clients should automatically reconnect and resynchronize.
15. Regional architecture
For global users:
US-East
US-West
Europe
Asia
Each region hosts its own collaboration cluster.
Document ownership is regional to minimize latency. Cross-region replication can provide disaster recovery and support users collaborating from different continents, though keeping collaborators on the same regional worker when possible reduces round-trip times.
Typical technology choices
- API Gateway: NGINX, Envoy, or HAProxy
- Realtime transport: WebSockets (or WebTransport as it matures)
- Collaboration engine: Rust, Go, Java, or C++ for efficient concurrency and memory usage
- Coordination: Consistent hashing plus a coordinator (or a service registry)
- Message log: Apache Kafka or Apache Pulsar
- Cache: Redis
- Metadata database: PostgreSQL or CockroachDB
- Object storage: Amazon S3, Google Cloud Storage, or Azure Blob Storage
- Search: OpenSearch or Elasticsearch
- Observability: OpenTelemetry with Prometheus and Grafana
Design principles that matter most
- Scale by document, not by user.
- Keep each document owned by a single collaboration worker to avoid distributed coordination.
- Store operations and generate periodic snapshots instead of rewriting whole documents.
- Keep presence (cursors, selections) separate from persisted document state.
- Treat collaboration workers as ephemeral: they can fail and recover from snapshots plus the operation log.
This architecture has many similarities to systems used by collaborative products like Figma, Google Docs, Notion, and Excalidraw, although each makes different choices around conflict resolution (CRDT vs. OT), persistence, and document partitioning based on its product requirements.