InterviewKata
System design · Worked design D12 of 46 · Social and content · Long form

Design file storage and sync

Save a file on your laptop and it appears on your phone a few seconds later; edit one slide in a 200 MB deck and only a sliver is uploaded. A sync service is a metadata problem dressed up as a storage problem: the bytes go to blob storage in content-addressed blocks, while a careful metadata service decides what every device should have. It follows the interview framework phase by phase.

1. The prompt

"Design Dropbox (or Google Drive): users keep a folder in sync across their devices, share files and folders with others, and can recover earlier versions."

Storing bytes is the easy half. The hard half is deciding, for every device, exactly what changed since it last looked; uploading only what changed; and never, ever losing or wrongly deleting someone's file when two devices disagree.

2. Frame the problem

How big are files, and how many users?

Files up to 50 GB; about 100M users with ~10 GB each.

An exabyte of logical data: deduplication and cheap storage matter. Big files must be split.

Do edits re-upload the whole file?

They shouldn't: a small edit should send a small upload.

Files become lists of blocks; only changed blocks move.

How fast must changes reach other devices?

Within seconds when they're online.

Devices need a push notification channel, not just polling.

Can devices work offline?

Yes, and sync when they reconnect.

Conflicts are inevitable; we need a policy that never loses data.

Sharing?

Files and folders, with other users and teams.

Shared folders become their own namespaces with permissions.

Version history and deleted files?

Yes, restorable for 30 days or more.

Old block lists are kept; garbage collection must respect retention.

Real-time co-editing inside documents?

No, that's a different product (Design D19).

We sync whole files, not keystrokes.

3. What it must do

  1. A user can upload, download, move, rename and delete files and folders from any device.
  2. Changes on one device appear on the user's other devices automatically.
  3. A user can share files and folders and restore earlier versions.

Not now:

4. How well it must do it

TargetWhy this number
Never lose data: no acknowledged file is ever lost; no sync bug deletes a file the user didn't delete data safetyA sync bug that deletes files is the worst outcome.
Efficient: a 1 MB edit in a 200 MB file uploads a few MB, not 200 where it runsUsers are on laptops on hotel Wi-Fi.
Fast propagation: other online devices see a change within ~5 s speedSync should feel like one folder everywhere.
Consistent per namespace: every device converges on the same tree consistencyTwo devices showing different folders erodes trust.
Private: users see only what they own or were shared securityIt's people's personal and business files.

5. Numbers that change the design

QuantityWorking→ so we…
Storage100M users × ~10 GB average used~1 EB logical → so blob storage with content-addressed blocks, dedup and erasure coding
Metadata100M users × ~5,000 files × ~1 KB~500 TB of rows → so a sharded database, partitioned by namespace
Change rate20M active users a day × 50 file changes1B changes a day ≈ 12,000/s → so notifications must be cheap per device
Edit efficiencyA 1 MB edit inside a 200 MB fileWith 4 MB blocks, upload ~4–8 MB instead of 200 MB

6. The nouns

blocks      sha256 → bytes in blob storage (+ reference counts for garbage collection)
files       (namespace_id, path) → current version
versions    version_id, namespace_id, path, size, block_list [sha256…], parent_version, author_device
journal     (namespace_id, seq) → change {path, version_id | deleted}
devices     device_id, user_id, cursor per namespace

7. The contract

REST for blocks and commits; a long-poll for change notifications. The user and device come from the session.

POST /v1/blocks/check {hashes: [...]}         → {missing: [...]}           (upload only these)
PUT  /v1/blocks/{hash}  (or a presigned URL)  → 201
POST /v1/files/commit {path, parent_rev, blocks: [...]} → {rev, seq} | 409 conflict
GET  /v1/changes?cursor=…                     → {entries: [...], cursor, has_more}
GET  /v1/changes/wait?cursor=…                → long-poll: returns when something changed
GET  /v1/blocks/{hash}                        → bytes (or a presigned URL)

8. First working version

A file API, a metadata database, blob storage, and clients that poll.

8.1 Saving a file

The client uploads the whole file to blob storage and records its path and storage key in the database.

8.2 Syncing other devices

Every 30 seconds each device asks for the list of files and their modification times, compares it with its own copy, and downloads whatever is newer.

8.3 Sharing and versions

A share row grants another user access to a folder; old storage keys are kept as versions.

Parked for later (section 9 fixes each)

  1. A one-character edit re-uploads the whole file; an interrupted upload starts over.
  2. Comparing whole folder listings every 30 s, from every device, is expensive and slow.
  3. A crash mid-upload can leave a half-written file.
  4. Two devices edit the same file offline.
  5. Storing identical files and versions many times over.
  6. Deletes, garbage collection and shared-folder hot spots.

9. Make it hold up

9.1 Blocks, hashes and uploading only what changed efficient · never lose data

ApproachWhy
✗ BadUpload the whole file on every saveA one-character edit to a 2 GB video project re-sends 2 GB; an interrupted upload starts over.
~ GoodSplit files into fixed-size blocks, identified by their hash; upload only blocks the server doesn't haveIn-place edits and appends send only touched blocks; identical blocks across versions are stored once. Dropbox describes this design with 4 MB blocks hashed with SHA-256.
✓ GreatAdd content-defined chunking where inserts are common, plus compression and resumable block uploadsBoundaries follow the content (a rolling hash), so inserting bytes near the start shifts only one chunk instead of every block after it. Try both in section 13.

Because a block's name is the hash of its contents, blocks are immutable: they can be cached forever, uploaded in parallel and retried safely, and a file version is just a list of hashes. Deduplicating across users has a privacy cost (an attacker can learn whether someone stores a known file by seeing whether its upload is skipped), so many services deduplicate only within one user's data or encrypt per user.

9.2 The metadata service is the heart never lose data · consistent

9.3 Telling devices something changed fast propagation

Polling every device every few seconds wastes requests at this scale. Devices hold a long-poll (or a WebSocket, or a mobile push) to a notification service, which only says "namespace N has changes". The device then pulls the journal from its cursor. The notification carries no data, so a lost notification costs only a delay (the device also checks periodically), never correctness.

9.4 Conflicts never lose data

Each commit includes the version it was based on (parent_rev); a commit based on an old version fails with a conflict. File sync tools don't merge binary content: the usual answer is to keep both, saving the loser as "report (conflicted copy).docx", and let the person choose. Deletes need care too: a stale device must never delete a file that another device has since edited.

9.5 Durability, versions, sharing and garbage collection never lose data · private

9.6 The sync engine on the client never lose data · consistent

Most data-loss bugs live in the client: reconciling the local disk, the last synced state and the remote journal, through renames, moves, crashes and full disks. Keep a local database of the last synced tree, apply remote changes and local changes as explicit, testable steps, and test with randomised simulations of crashes and concurrent edits. Dropbox wrote publicly about rewriting its sync engine for exactly these reasons.

9.7 The whole design after the deep dives

One edit on a laptop reaching a phone through the final design.

10. Interviewer follow-ups

A shared folder with 50,000 members at a big company.

One namespace gets heavy commit traffic and fan-out of notifications. Split very large shared trees into sub-namespaces, rate-limit commits per namespace, and batch notifications so devices pull once for many changes.

A user uploads a 50 GB file over a weak connection.

Blocks upload independently and resumably; the client tracks which are done. Only after all are present does it commit. Upload in parallel with a limit, and back off when the network is saturated.

A device was offline for a month.

It pulls the journal from its old cursor. If the journal for that range has been compacted away, it falls back to a full listing compared against its local database, which is slower but correct.

Ransomware encrypts every file on a laptop, and sync uploads them.

Version history lets the user restore the namespace to a point in time. Detect mass rewrites (thousands of files changed in minutes, high entropy) and offer a one-click restore.

Cut storage cost.

Deduplicate within accounts, compress blocks, erasure-code instead of triple replication, and move old versions and cold files to cheaper tiers.

LAN sync between devices in the same office.

Devices discover each other locally and fetch blocks by hash from a peer instead of the cloud. Hashes make this safe: a block is verified by its name.

Client-side encryption.

Encrypt blocks with keys the server never sees. It costs cross-user dedup, server-side previews and search, and makes key recovery a product problem.

11. When things break

FailureWhat users seeHow we detect itWhat limits the damage
Crash mid-uploadUpload resumes laterMissing blocks at commitCommit only after all blocks; orphan blocks GC'd
Notification service downChanges appear more slowlyConnection errorsDevices fall back to periodic journal checks
Metadata shard downOne namespace can't syncErrors per shardReplicated database with failover; clients retry
Concurrent offline editsA "conflicted copy" appears409 on commitKeep both; never overwrite silently
GC bugMissing file contentRestore and audit checksRetention period, reference-count audits, staged deletes

12. Wrap-up and the clock

Trade-offs we made:

What we'd watch:

At 10×:

A 45-minute interview

0–4Frame the problem (sizes, offline, sharing)
4–9Must do, how well, numbers
9–13Nouns and contract
13–20First working version, parked list
20–40Deep dives: blocks (9.1), metadata and journal (9.2), notifications (9.3), conflicts (9.4)
40–45Follow-ups and wrap-up

A 60-minute interview

0–5Frame the problem
5–11Must do, how well, numbers
11–16Nouns and contract
16–24First working version
24–50Deep dives 9.1–9.6 and the final picture
50–60Follow-ups: big shared folders, ransomware, LAN sync; wrap-up
LevelWhat a strong answer shows
Mid-levelFiles in blob storage, metadata in a database, uploads via presigned URLs, devices polling for changes.
SeniorContent-hashed blocks, upload only missing blocks, commit-after-upload, a per-namespace journal with cursors, long-poll notifications, conflict copies.
Staff+Chunking strategy and dedup privacy, garbage-collection safety, sharding by namespace and hot shared folders, client sync-engine correctness and testing, storage cost tiers.

Common mistakes:

13. Try it, check yourself, read more

This is a real 64 KB file, chunked in your browser two ways: fixed 4 KB blocks, and content-defined chunks (a gear rolling hash, average about 4 KB). Make an edit and see which chunks must be uploaded again (orange).

Fixed 4 KB blocks
Content-defined chunks

How does a sync client avoid re-uploading a whole file after a small edit?

Files are lists of content-hashed blocks; unchanged blocks are already stored.

Why does content-defined chunking help with inserts?

Boundaries are chosen by a rolling hash of the bytes, so they re-sync right after the edit.

A device wakes after a day offline. How does it catch up?

The journal is an ordered log of changes; the cursor says where the device left off.

Two devices edited the same file offline. What does a sync service usually do?

Never lose either person's work; let them choose.
ResourceWhy
Streaming file synchronization (Dropbox)Blocks, SHA-256 blocklists and how uploads and downloads overlap.
Rewriting the heart of our sync engine (Dropbox)Why the client's sync logic is the hard part, and how they made it testable.
FastCDC (USENIX ATC 2016)A fast content-defined chunking algorithm using a gear hash, like the one in the lab.