Notion#
Problem statement (interviewer prompt)
Design Notion: a block-based collaborative document + wiki + lightweight database (tables, kanban, calendar). Each block is a tree node, multiple users edit concurrently, and pages can reference other pages. Support offline edit + sync, permissions, and full-text search.
flowchart LR
U([User])
BLK[Block Service]
TREE[(Block tree per page)]
DB[Database / View]
PG[Page Renderer]
U --> BLK --> TREE
BLK --> DB
TREE --> PG --> U
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class U client;
class BLK,PG service;
class TREE,DB datastore;
flowchart TB
subgraph Clients
WEB
MOB
DESK[Desktop]
end
subgraph Edge
LB
WS[WebSocket gateway]
end
subgraph Core
BLK[Block Service<br/>everything is a block]
PARENT[Tree maintenance<br/>parent / children pointers]
DBV[Database / View Service<br/>tables, kanban, calendar]
PROPS[Property types]
PERMS[Permissions inherited from parent]
end
subgraph Sync[Sync engine]
REC[Record transactions]
QU[[Per-record ops queue]]
OFFLINE[Offline ops]
SYNC([Sync engine reconcile])
end
subgraph Storage
PG[(Postgres - blocks as rows)]
META[(Workspace metadata)]
FILES[(Files / S3)]
SEARCH[(Search index)]
end
subgraph Features
COMM[Comments / mentions]
REM[Reminders / notifications]
TPL[Templates]
AI[Notion AI]
SYNCED[Synced blocks across pages]
end
Clients --> Edge --> Core --> Storage
Sync --- Core
Features --- Core
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class WS edge;
class DESK,BLK,PARENT,PROPS,PERMS,REC,OFFLINE,COMM,REM,TPL,AI,SYNCED service;
class DBV,PG,META,SEARCH datastore;
class QU queue;
class SYNC compute;
class FILES storage;
Block model#
- Everything (page, paragraph, todo, image, db) is a block with
id,parent_id,type,properties,content[]. - Tree edits cascade in deterministic order; permissions inherited.
Sync engine#
- Optimistic local writes; reconciliation on sync.
- Conflict resolution mostly LWW per property; tree structure uses parent-aware merge.
Glossary & fundamentals#
Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.
| Tag | Concept | What it is | Page |
|---|---|---|---|
HLD |
Realtime protocols | WS / SSE / polling / gRPC streaming | realtime-protocols |
Quick reference#
Functional#
- Block-based pages with rich content.
- Inline databases with multiple views.
- Real-time multi-user editing.
- Comments, mentions, reminders.
- Offline support.
- AI / search.
Non-functional#
- p99 sync < 1 s.
- Workspaces up to 10s of thousands of users.
- Offline editing must reconcile cleanly.
Trade-offs#
- Block as universal primitive = expressive but verbose.
- Postgres at scale demands sharding (workspace).
- Tree with permissions inheritance: change at parent cascades.
Refs#
- Notion engineering blog ("Sharding our Postgres", "Building Notion AI").
- ByteByteGo "Design Notion".
FAQ#
How does Notion store documents?#
Every page is a tree of blocks where each block is a row in a relational store with parent_id, type, and properties. Rendering walks the tree and resolves block references on the fly.
What is the block model in Notion?#
A block is a small typed unit (paragraph, heading, todo, table row, embed). Pages, databases, and views are all built from the same block primitive linked by parent and child references.
How does Notion handle real-time collaboration?#
Block edits are streamed over WebSocket and applied with last-write-wins per property. Concurrent edits on different properties of the same block compose cleanly without a merge UI.
How does Notion implement databases and views?#
A database is a block whose children are entry blocks. A view is metadata pointing at the database with filter, sort, group, and layout (table, kanban, calendar) interpreted at render time.
How does Notion handle offline editing?#
Clients keep a local cache of recently viewed blocks and queue edits with a logical clock. On reconnect they replay against the server, which dedupes by op id and applies in order.
Related Topics#
- Google Docs: OT-based collaborative editor predecessor
- Figma: CRDT-based collaborative design
- Collaborative Editor: general OT vs CRDT trade-offs
- CRDTs: the conflict-free replicated data type primitive