Design Google Drive: Chunking, Sync, and Real-Time Collaboration
How to design Google Drive in a system design interview — content-addressed chunking, block-level sync, ACLs, and real-time collaboration with real capacity numbers.
This article has an interactive companion
Don't just read it — step through it interactively, one state change at a time.
Google Drive looks like a folder that happens to live on the internet. Underneath, it is three hard problems stitched together: storing exabytes of blobs cheaply, keeping a desktop client and a server byte-for-byte in agreement without moving whole files, and letting a dozen people type into the same document at once without stepping on each other.
The interview reward comes from refusing to treat it as one big blob store. The moment you split metadata (the file tree, versions, permissions) from content (the actual bytes), most of the design falls out naturally. Get that separation right and everything else — dedup, resumable uploads, cheap copies, sync — is a consequence.
What the system actually has to do
Start by naming the caller-facing promises, because they pick your datastores for you.
Functional requirements: a user can upload and download files, organize them into folders, get a synced copy on every device, share a file or folder with specific people or "anyone with the link," see and restore old versions, and search their Drive by filename and content. For the Docs-style products layered on top, multiple users edit the same document live.
Non-functional requirements are where the interesting bounds live:
- Durability is non-negotiable — losing a user's file is a company-ending event, so target 11 nines and replicate blobs across zones.
- Availability over strict consistency for reads: seeing a file a few seconds late is fine; seeing the wrong bytes is not.
- Sync latency: a change on one device should appear on another within a couple of seconds when both are online.
- Bandwidth efficiency: editing 100 KB of a 1 GB file must not re-upload 1 GB.
That last bullet is the whole ballgame, and it forces the storage model.
Why Drive chunks files instead of storing them whole
A naive design stores each file as one object keyed by file ID. It works until someone edits a large file, and now every save round-trips the entire thing.
Drive and Dropbox instead split every file into chunks — commonly around 4 MB — and store each chunk under the hash of its own contents (SHA-256). The file itself becomes a manifest: an ordered list of chunk hashes plus metadata. Content is addressed by what it is, not by which file it belongs to.
That one decision buys four properties for free:
| Property | Why chunking gives it |
|---|---|
| Deduplication | Identical chunks hash to the same key, so the same email attachment shared by 500 people is stored once. |
| Resumable uploads | The client asks "which of these hashes do you already have?" and sends only the missing ones. |
| Cheap copies | Duplicating a file clones its manifest — no bytes move. |
| Block-level sync | Editing part of a file rewrites only the affected chunks. |
Fixed-size chunking has one weakness: insert a byte near the front and every chunk boundary downstream shifts, so every chunk re-hashes as "new." Content-defined chunking fixes this by choosing boundaries with a rolling hash (Rabin fingerprint) over a sliding window, so a boundary lands on a content pattern. Insert bytes and the boundaries after the edit stay put — only the touched region re-chunks.
Block-level sync, traced through one edit
The bandwidth win is concrete. Walk a single scenario: a user has a synced 1 GB video, opens it, and edits 100 KB somewhere in the middle.
| Step | State | What moves |
|---|---|---|
| Baseline | File is 256 chunks of 4 MB; the client stored last-uploaded manifest M0 = [h1, h2, … h256]. | nothing |
| Local edit | User changes 100 KB inside the region covered by chunk h130. | nothing yet |
| Re-chunk | Client re-hashes the file; with content-defined chunking only the window around the edit changes → h130 becomes h130'. | nothing yet |
| Diff manifests | New manifest M1 = [h1 … h129, h130', h131 … h256]. Diff vs M0 shows 1 changed hash. | nothing yet |
| Upload | Client asks the server which hashes are missing; only h130' is. It uploads ~4 MB plus the tiny M1. | ~4 MB |
| Commit | Server writes a new version pointing at M1. Old chunk h130 stays until garbage collection. | 0 |
Naive whole-file sync moves 1 GB. Block-level sync moves roughly 4 MB — about 0.4% of the file. The client keeps a local SQLite index of the current manifest and a file-watcher, so re-hashing is incremental rather than a full re-scan on every save.
Capacity estimation with real numbers
Interviewers use capacity math to see whether your storage choices survive scale. Take a Drive-sized target: 2 billion users, 15 GB stored on average, 20 file operations per user per day, with peaks around 5x the average rate.
Raw storage: 2B × 15 GB = 30 EB before dedup. Dedup plus compression empirically saves 20–40%, landing physical storage around 18–24 EB. This has to be an object store (GCS/S3-class), not a database.
Chunk count: 15 GB ÷ 4 MB ≈ 3,840 chunks per user × 2B users ≈ 7.7 trillion chunks. The manifest/chunk index is itself a trillion-row problem, which is why it lives in a sharded key-value store, not SQL.
Throughput: 2B × 20 ops ÷ 86,400 s ≈ 460K ops/sec average, ≈ 2.3M ops/sec at peak. No single metadata database serves that; you partition the metadata service, typically by user ID or by the root of the file tree, so one user's whole subtree lands on one shard and tree operations stay local.
The takeaway you say out loud: blobs and metadata scale on completely different axes. Blobs want a flat, near-infinite object store; metadata wants many small, transactional shards.
Architecture and data model
The request path splits cleanly along the metadata/content seam.
- Metadata service — the file/folder tree, versions, and manifests. Transactional per user so a rename or move is atomic. Sharded by user.
- Blob/chunk service — content-addressed chunks in object storage, replicated across zones.
- Sync service — reconciles a client's known state with the server's, using a per-user monotonic change cursor so a client can ask "what changed since cursor 4471?" and get a delta.
- Sharing/ACL service — permission checks, covered below.
- Search service — an inverted index over extracted text.
- Collaboration service — the live-editing layer for Docs.
A minimal data model:
-- One row per logical file, current pointer lives on the head version
CREATE TABLE files (
file_id BIGINT PRIMARY KEY,
owner_id BIGINT NOT NULL,
parent_id BIGINT, -- folder; NULL at root
name TEXT NOT NULL,
head_version BIGINT
);
-- Immutable version history; each points at a manifest
CREATE TABLE versions (
version_id BIGINT PRIMARY KEY,
file_id BIGINT NOT NULL,
manifest_id BIGINT NOT NULL,
created_at TIMESTAMPTZ NOT NULL
);
-- Manifest = ordered chunk hashes for one version
CREATE TABLE manifest_chunks (
manifest_id BIGINT NOT NULL,
seq INT NOT NULL, -- chunk order in file
chunk_hash BYTEA NOT NULL, -- SHA-256, points into blob store
PRIMARY KEY (manifest_id, seq)
);Uploads never write blob bytes through the API tier. The client requests a presigned URL and streams chunks straight to object storage, then the metadata service commits the manifest once all chunks land. That keeps the synchronous path short and the API tier stateless.
Deep dive: sharing and permissions
A file can be shared with an individual, a group, an entire domain, or "anyone with the link." Model an ACL as a list of (principal, role) pairs, where a principal is a user, group, domain, or link token, and a role is viewer/commenter/editor. Folders carry ACLs too, and children inherit unless overridden.
The hard part is evaluation. A user's effective role on a file is the maximum role across every principal that applies to them, including transitive group membership — you belong to a group that belongs to a group that was granted editor. Expanding that membership graph on every read is too slow, which is exactly the problem Google's Zanzibar system solves: it denormalizes relations into a fast check(user, relation, object) API and uses consistency tokens so a client can demand "evaluate this against a snapshot at least as fresh as my last write." Membership expansions are cached and invalidated by change events; inherited folder permissions are evaluated lazily and materialized for hot paths.
Deep dive: real-time collaboration
Live document editing needs concurrent edits to converge to the same result on every client. Two families solve it.
| Operational Transform (OT) | CRDT | |
|---|---|---|
| How it converges | Central server transforms each incoming op against concurrent ops so indices stay valid | Each character/op carries a unique ID; merges are commutative |
| Needs a server | Yes | No — works peer-to-peer |
| Per-edit size | Tiny (a few bytes) | Larger — per-character metadata and tombstones |
| Best fit | Central, online editing at scale | Offline-first, p2p, local-first apps |
Google Docs uses OT. The reasoning is workload-specific: Docs already has a central server, edits are tiny insert/delete ops that persist cheaply as a compact log, and transforming against concurrent ops keeps character indices consistent. CRDTs win when there is no central authority — offline-first note apps and collaborative editors that sync device-to-device — but you pay in per-character metadata and tombstone garbage collection.
Deep dive: conflicts and search
Offline conflicts on binary files. Two users edit the same photo offline. Binary files can't be three-way merged, so on reconnect the client compares its manifest's parent version against the server's current head. If they diverge, it uploads its edit as a new version and writes a separate "conflicted copy" file (the name (user's conflicted copy 2026-07-17) you've seen) so neither branch is lost. The invariants: never lose data, always converge the server to one linear version chain, and make the conflict visible instead of silently clobbering.
Full-text search under ACLs. A pipeline extracts text from uploads (PDF OCR, Office parsing) and feeds an indexer that writes per-tenant shards with the document's ACL principals materialized onto each entry. At query time you expand the searcher's principals and apply them as a filter clause, so results are permission-correct by construction. Fresh writes land in a small in-memory "recent" index merged in at query time, so a just-uploaded file is searchable within seconds; compaction folds it into the main index later. An ACL change enqueues a reindex to update the denormalized principals on affected docs.
How this shows up in interviews
The signal an interviewer is hunting for is whether you split metadata from content early and let it drive everything after. If you propose "store each file as one blob keyed by file ID," expect the follow-up "now the user edits 100 KB of a 1 GB file" and watch your design fall over.
Strong answers reach for chunking and manifests unprompted, then defend the 4 MB chunk size, explain content-defined vs fixed boundaries, and show how the change cursor drives incremental sync. Common probes: how dedup interacts with per-user encryption, how you garbage-collect chunks no manifest references anymore, how the metadata shard key avoids a hot partition when one user has millions of files, and OT vs CRDT for the collaboration layer. Trace one upload and one edit end-to-end, name the source of truth at each hop, and state which trade-off you'd reverse at a different scale.
Further learning
- Design Dropbox / Google Drive w/ Ex-Meta Staff Engineer — Hello Interview's full walkthrough of this exact problem.
- Practice this topic on YeetCode — the interactive roadmap version.
Related builds in this track: Design Ticketmaster, Design a Payment System, Design a Search Engine, and Design Top K Problem.
FAQ
Why store files as content-addressed chunks instead of whole objects?
Because addressing a chunk by the hash of its bytes makes identical data collapse to one copy automatically. The same attachment shared by hundreds of users, or an unchanged region of a file across versions, is stored exactly once. It also enables resumable uploads (send only the hashes the server lacks), near-free copies (clone the manifest), and block-level sync (rewrite only changed chunks). A whole-object model gives you none of these and re-transfers entire files on every edit.
How much data does block-level sync actually save?
Editing 100 KB in the middle of a 1 GB file moves roughly one 4 MB chunk plus a tiny manifest — about 0.4% of the file — instead of the full gigabyte. The client re-hashes locally, diffs the new manifest against the last-uploaded one, and uploads only the chunk hashes the server is missing. With content-defined chunking, an insertion doesn't shift every downstream boundary, so the changed set stays small even when byte offsets move.
Why does Google Docs use Operational Transform instead of CRDTs?
Because the workload already has a central server and edits are tiny. OT sends small insert/delete operations through that server, which transforms each against concurrent ops so character indices stay consistent, and persists them as a compact log. CRDTs attach unique IDs to every character so merges commute without a central authority — great for offline-first and peer-to-peer editing — but they carry heavier per-character metadata and need tombstone garbage collection. At Docs scale, OT's small ops and existing server tip the trade-off.
How does search stay permission-correct and fresh at the same time?
Each indexed document carries its ACL principals materialized onto the entry, so a query filters on the searcher's expanded principals and can never return a file they can't access. Freshness comes from a two-tier index: new uploads land in a small in-memory "recent" index merged in at query time, making them findable within seconds, while background compaction rolls them into the main index. ACL changes trigger a reindex to update the denormalized principals on affected documents.
How do you estimate storage for a Drive at billions of users?
Multiply users by average bytes stored: 2B users × 15 GB ≈ 30 EB raw. Dedup and compression typically reclaim 20–40%, leaving roughly 18–24 EB of physical storage in an object store. Separately, chunk count drives the metadata size: 15 GB ÷ 4 MB ≈ 3,840 chunks per user × 2B ≈ 7.7 trillion chunks, which is why the manifest index is a sharded key-value store rather than SQL. Throughput lands near 460K ops/sec average and 2.3M at peak, forcing the metadata service to partition by user.
Make it stick: run this one yourself
Don't just read it — step through it interactively, one state change at a time.