01

Requirements & scope

Problem Statement & Requirements

Design a cloud file synchronization service that keeps files consistent across multiple devices, supports sharing and collaboration, and provides reliable cloud storage — similar to Dropbox, Google Drive, OneDrive, or iCloud Drive.

Functional Requirements

  • Upload / download files — store files of any type up to 50 GB in the cloud
  • Automatic sync — changes on one device automatically propagate to all other devices linked to the same account
  • Offline support — users can edit files offline; changes sync when connectivity resumes
  • File versioning — maintain history of file versions; revert to any previous version
  • Sharing — share files/folders with other users via link or direct permission (view/edit)
  • Conflict resolution — handle simultaneous edits to the same file on different devices
  • Folder structure — hierarchical folders with move, rename, delete operations
  • Notifications — notify users when shared files are modified
  • Search — search files by name, content, and metadata
  • Trash / recovery — deleted files recoverable for 30 days

Non-Functional Requirements

  • Consistency — all devices must eventually converge to the same state
  • Low latency sync — changes propagate to other devices within seconds
  • Bandwidth efficiency — only transfer changed portions of files (delta sync), not entire files
  • Reliability — 99.999999999% (11 nines) durability for stored files
  • Scalability — 500M users, 100B files, exabytes of storage
  • Availability — 99.99% for upload/download; sync can tolerate brief delays
  • Deduplication — identical files/blocks stored once (cross-user dedup)

Out of Scope

  • Real-time collaborative editing (Google Docs — see Real-Time Collaboration design)
  • Photo-specific features (albums, face recognition)
  • Video transcoding or streaming
  • Enterprise DLP / compliance features
02

Scale estimations

Scale Estimations

Traffic

MetricValue
Total users500M
Daily active users100M
Average files per user200
Total files500M × 200 = 100B files
Average file size500 KB (heavily skewed: many small, few large)
File uploads / day500M (new + modified)
File uploads / second~5,800 RPS
File downloads / second~15,000 RPS
Sync metadata operations / second~100K RPS (file list, status checks)
Peak sync traffic (9am workday start)3× average

Storage

MetricValue
Total raw file storage100B × 500 KB = ~50 PB
With deduplication (~30% savings)~35 PB
With replication (3×)~105 PB raw storage
File metadata per file500 bytes (name, size, hash, timestamps, permissions)
Total metadata100B × 500 B = ~50 TB
Version history (avg 5 versions/file)500B versions × 200 B each = ~100 TB metadata
Block metadata (for chunked files)~20 TB

Bandwidth

MetricValue
Upload bandwidth (5.8K/s × 500 KB avg)~2.9 GB/s
Download bandwidth (15K/s × 500 KB avg)~7.5 GB/s
Delta sync savings (~70% less data transferred)Effective: ~3 GB/s total
Sync notification bandwidth~50 MB/s

Hardware Estimate

ComponentSpec
API / sync servers100-200 (stateless)
Metadata database50-100 shards (SSD-backed PostgreSQL)
Block storage (object store)Thousands of nodes (HDD-based, S3-style)
Notification / sync push50-100 WebSocket servers
Dedup index20-30 nodes (hash → block_id lookup)
Search index30-50 nodes (Elasticsearch)
03

Layered architecture

Phase 1 of 4 · Client & edge

Start with the entry points

First, define who uses the system and where requests enter. This is the boundary of the design, not yet a complete request path.

Sync client
Watch local changes, chunk files, and keep a local manifest to detect what actually changed.
Sync API
Authenticate operations and separate metadata traffic from bulk block transfer.
Architecture after phase 1. Includes Sync client, Sync API. Sync client connects to Sync API.
Phase 1 — Start with Sync client, Sync API.SVGSVGEditable diagramEditable diagram

This first layer defines the system boundary. The next chapters add application logic and data dependencies. After examining the request paths and tradeoffs, we will add operational safeguards and bring the complete architecture together.

04

API & contracts

Phase 2 of 4 · Application & compute

Add the application logic

Keep the entry points and add the services that do the work. Give each service a clear responsibility before defining its contracts. Storage and background dependencies come next.

Metadata service
Use version checks to detect concurrent updates rather than blindly applying last-write wins.
Block service
Upload only chunks absent from storage and validate content hashes.
Architecture after phase 2. Includes Sync client, Sync API, Metadata service, Block service. Sync client connects to Sync API. Sync API connects to Metadata service. Sync API connects to Block service.
Phase 2 — Added Metadata service, Block service. Earlier layers remain in place.SVGSVGEditable diagramEditable diagram

API Design

Upload File (Chunked)

EXAMPLE
Step 1: Initiate Upload
POST /api/v1/files/upload
Authorization: Bearer <token>

Request:
{
  "path": "/Documents/report.pdf",
  "size": 15728640,                    // 15 MB
  "checksum": "sha256:a1b2c3d4...",
  "modified_at": "2026-04-03T10:00:00Z",
  "parent_folder_id": "folder-docs-123"
}

Response (200 OK):
{
  "file_id": "file-abc123",
  "upload_id": "upl-xyz789",
  "blocks_needed": [                   // server tells client which blocks it doesn't already have
    {"block_hash": "sha256:block1...", "offset": 0, "size": 4194304},
    {"block_hash": "sha256:block3...", "offset": 8388608, "size": 4194304},
    // block2 already exists (dedup!) — not listed
  ],
  "upload_urls": [                     // presigned URLs for direct-to-storage upload
    {"block_hash": "sha256:block1...", "url": "https://storage.example.com/upload/..."},
    {"block_hash": "sha256:block3...", "url": "https://storage.example.com/upload/..."}
  ]
}

Step 2: Upload Blocks (parallel, direct to object storage)
PUT https://storage.example.com/upload/...
Content-Type: application/octet-stream
<block binary data>

Response: 200 OK, ETag: "block1-etag"

Step 3: Commit Upload
POST /api/v1/files/upload/{upload_id}/commit
{
  "blocks": [
    {"block_hash": "sha256:block1...", "etag": "block1-etag"},
    {"block_hash": "sha256:block2...", "etag": "existing"},    // deduped
    {"block_hash": "sha256:block3...", "etag": "block3-etag"},
    {"block_hash": "sha256:block4...", "etag": "existing"}
  ]
}

Response (200 OK):
{
  "file_id": "file-abc123",
  "version": 3,
  "size": 15728640,
  "modified_at": "2026-04-03T10:00:00Z"
}

Download File

EXAMPLE
GET /api/v1/files/{file_id}/content
Authorization: Bearer <token>
Range: bytes=0-4194303        // optional: download specific block range

Response (200 OK / 206 Partial):
Content-Type: application/pdf
Content-Length: 15728640
<binary file data>

OR for large files, client downloads blocks in parallel:
GET /api/v1/files/{file_id}/blocks
→ Returns list of block hashes + presigned download URLs
→ Client downloads blocks in parallel, assembles locally

Get File Changes (Sync)

EXAMPLE
GET /api/v1/sync/changes?cursor=eyJsYXN0IjoiY2hhbmdlXzEyMzQ1In0=

Response (200 OK):
{
  "changes": [
    {
      "type": "file_modified",
      "file_id": "file-abc123",
      "path": "/Documents/report.pdf",
      "version": 3,
      "size": 15728640,
      "checksum": "sha256:a1b2c3d4...",
      "modified_at": "2026-04-03T10:00:00Z",
      "modified_by": "user-alice"
    },
    {
      "type": "file_created",
      "file_id": "file-def456",
      "path": "/Photos/vacation.jpg",
      "version": 1,
      "size": 3145728
    },
    {
      "type": "file_deleted",
      "file_id": "file-ghi789",
      "path": "/Documents/old-draft.docx"
    },
    {
      "type": "folder_moved",
      "folder_id": "folder-jkl012",
      "old_path": "/Projects/2025",
      "new_path": "/Archive/Projects-2025"
    }
  ],
  "cursor": "eyJsYXN0IjoiY2hhbmdlXzk5OTk5In0=",
  "has_more": false
}

// Client uses cursor for incremental sync
// On first sync: cursor=null → returns all files
// Subsequent syncs: cursor from last response → returns only changes since

Share File

EXAMPLE
POST /api/v1/files/{file_id}/share
{
  "shared_with": [
    {"user_id": "user-bob", "permission": "edit"},
    {"user_id": "user-charlie", "permission": "view"}
  ],
  "link_sharing": {
    "enabled": true,
    "permission": "view",
    "expires_at": "2026-05-01T00:00:00Z",
    "password": "optional-password"
  }
}

Response (200 OK):
{
  "share_id": "share-xyz",
  "link": "https://drive.example.com/s/abc123xyz",
  "permissions": [
    {"user_id": "user-bob", "permission": "edit"},
    {"user_id": "user-charlie", "permission": "view"}
  ]
}
05

Data model

Phase 3 of 4 · Data & async work

Connect data and background work

The services now need their dependencies: durable state, fast lookups, or work that continues outside a request. Separate these responsibilities rather than treating every component as a database.

Metadata database
Persist ordered file revisions and references to chunks so clients can resume safely.
Object storage
Store immutable encrypted blocks and reference them from file manifests.
Notification service
Notify other devices of changed revisions; clients still reconcile against the authoritative manifest.
Architecture after phase 3. Includes Sync client, Sync API, Metadata service, Block service, Metadata database, Object storage, Notification service. Sync client connects to Sync API. Sync API connects to Metadata service. Sync API connects to Block service. Metadata service connects to Metadata database. Block service connects to Object storage. Metadata service connects to Notification service.
Phase 3 — Added Metadata database, Object storage, Notification service. Earlier layers remain in place.SVGSVGEditable diagramEditable diagram

Data Model

File Metadata

File Metadata
File MetadataSVGSVGEditable diagramEditable diagram

File Version History

File Version History
File Version HistorySVGSVGEditable diagramEditable diagram

Block (Content-Addressable Storage)

Block (Content-Addressable Storage)
Block (Content-Addressable Storage)SVGSVGEditable diagramEditable diagram

Sync Journal (Change Log)

Sync Journal (Change Log)
Sync Journal (Change Log)SVGSVGEditable diagramEditable diagram
06

Core design decisions

Core Design Decisions

Decision 1: File Chunking Strategy

The most important design decision. How files are split into blocks determines dedup efficiency, transfer efficiency, and delta sync capability.

Option A: Fixed-Size Chunking
Option A: Fixed-Size Chunking
Option A: Fixed-Size ChunkingSVGSVGEditable diagramEditable diagram
ProsCons
Simple implementationInsertions/deletions shift all subsequent blocks
Predictable block sizesPoor delta efficiency for text files
Good for append-only filesNo dedup for shifted content
Option B: Content-Defined Chunking (CDC) — Recommended
Option B: Content-Defined Chunking (CDC) — Recommended
Option B: Content-Defined Chunking (CDC) — RecommendedSVGSVGEditable diagramEditable diagram
ProsCons
Only changed chunks re-uploadedVariable chunk sizes (slightly more complex)
Excellent dedup across file versionsCPU cost for rolling hash computation
Handles insertions/deletions gracefullyMin/max bounds add implementation complexity
Cross-file dedup (same content → same hash)
Recommendation: Content-Defined Chunking (CDC)

This is what Dropbox uses. The delta efficiency alone saves 60-80% of upload bandwidth.

Decision 2: Sync Protocol — How Devices Stay in Sync

Decision 2: Sync Protocol — How Devices Stay in Sync
Decision 2: Sync Protocol — How Devices Stay in SyncSVGSVGEditable diagramEditable diagram

Decision 3: Deduplication Architecture

Decision 3: Deduplication Architecture
Decision 3: Deduplication ArchitectureSVGSVGEditable diagramEditable diagram
07

Request flows

Detailed Flow Diagrams

File Upload (Edit) Flow

File Upload (Edit) Flow
File Upload (Edit) FlowWide diagram — scroll horizontally.SVGSVGEditable diagramEditable diagram

Sync to Other Devices

Sync to Other Devices
Sync to Other DevicesWide diagram — scroll horizontally.SVGSVGEditable diagramEditable diagram

Conflict Resolution Flow

Conflict Resolution Flow
Conflict Resolution FlowSVGSVGEditable diagramEditable diagram
08

Performance & caching

Block Storage & Deduplication

Storage Architecture

Storage Architecture
Storage ArchitectureSVGSVGEditable diagramEditable diagram

Dedup Savings Calculation

Dedup Savings Calculation
Dedup Savings CalculationSVGSVGEditable diagramEditable diagram
09

Advanced design

Security & Encryption

Security & Encryption
Security & EncryptionSVGSVGEditable diagramEditable diagram

Smart Sync / On-Demand Files

Smart Sync / On-Demand Files
Smart Sync / On-Demand FilesSVGSVGEditable diagramEditable diagram
10

Edge cases

Handling Edge Cases

Large File Upload (10 GB+)

EXAMPLE
Problem: 10 GB file upload over unstable network

Solution: Resumable chunked upload

  1. Client chunks into 4 MB blocks (2,500 blocks for 10 GB)
  2. Client uploads blocks in parallel (4-8 concurrent uploads)
  3. Each block upload is independent:
     → If one fails, retry just that block (not entire file)
  4. Server tracks which blocks received via upload_id
  5. Client can query: GET /upload/{id}/status → {received: [h1,h2,...]}
  6. On connection drop: client resumes uploading missing blocks
  7. After all blocks received → commit

  Progress tracking:
  → Client shows: "Uploading report.zip: 1,847 / 2,500 blocks (73%)"
  → Accurate even after reconnection (server knows exact state)

Rename/Move Folder with 100K Files

EXAMPLE
Problem: User renames "/Projects" to "/Archive/Projects"
  → If we update 100K file paths individually = slow + risky

Solution: Path is computed, not stored per-file

  Option A: Store parent_folder_id (not full path)
    File stores: parent_folder_id = "folder-projects"
    Renaming folder = update ONE folder record
    → All children automatically resolve to new path via traversal
    → But: path computation on every read (join chain)

  Option B: Materialized path + batch update (Dropbox approach)
    Store full path per file (denormalized for fast reads)
    Rename = batch update all children:
      UPDATE files SET path = REPLACE(path, '/Projects', '/Archive/Projects')
      WHERE path LIKE '/Projects%' AND owner_id = ?
    → One SQL statement, batched (100K rows in < 1 second)
    → Sync journal: one "folder_moved" entry (not 100K entries)
    → Other devices apply the rename locally (not re-downloading files)

  Recommendation: Materialized path (Option B) — faster reads, bulk update

Account Runs Out of Storage

EXAMPLE
Quota: 2 TB for premium user, currently at 1.98 TB

Behavior:
  1. Upload attempted → would exceed quota → 413 Payload Too Large
  2. Client shows: "Storage full (1.98 / 2.0 TB)"
  3. Sync from other devices still works (download)
     → But new uploads blocked
  4. File deletions still work → free up space
  5. Grace period: if user exceeds quota (e.g., shared folder pushed over):
     → 30-day grace to delete files or upgrade
     → After 30 days: oldest files in trash permanently deleted
     → Never delete files outside trash without user consent

  Dedup-aware quota:
  → Blocks shared via dedup DON'T double-count against quota
  → User's quota = sum of unique blocks in their files
  → If block also in another user's files → counts for both users
    (conservative; ensures deleting one user's account doesn't
     leave the other over-quota)

Device Completely Out of Sync (New Device or Long Offline)

EXAMPLE
Scenario: New laptop connected to account with 200K files

Full sync strategy:
  1. Fetch file metadata tree (paginated, all 200K entries)
     → ~100 MB of metadata (200K × 500 bytes)
     → Downloaded in ~10 seconds

  2. Priority-based block download:
     → Recently modified files first
     → Starred/favorited files next
     → Largest files last
     → User can work while sync continues in background

  3. "Smart Sync" / on-demand files (Dropbox Smart Sync):
     → Show all files in filesystem (placeholder files)
     → File appears as if it exists locally (has name, size, icon)
     → Actual content downloaded only when user OPENS the file
     → Saves: 200K files × 500 KB avg = 100 GB of disk space
     → Only ~5% of files are actively accessed → download ~5 GB

  Implementation:
  → Cloud Files API (FUSE on Linux, kernel extension on macOS/Windows)
  → Intercept file open() → trigger download → serve content
  → Mark frequently accessed files as "always available offline"
11

Tradeoffs

Tradeoffs & Design Decisions Summary

DecisionOption AOption BChosenWhy
ChunkingFixed-size blocksContent-defined (CDC)CDCHandles insertions gracefully; only changed chunks re-uploaded; cross-version dedup
Sync protocolFull-state comparisonJournal/cursor-basedJournal + cursorIncremental; O(changes) not O(total_files); cursor enables resume
Sync notificationPeriodic pollingLong-poll / WebSocket pushPush + poll fallback< 1 second notification; polling as reliability backup
DeduplicationPer-user onlyGlobal cross-userGlobal (authenticated)30-50% storage savings; convergent encryption for security
Block uploadThrough API serverDirect to object storage (presigned URL)Direct + presignedAvoids proxy bottleneck; API server handles only metadata
Conflict resolutionLast-writer-winsConflict copyConflict copyNo silent data loss; user decides; version history preserves all changes
File path storageComputed from parent chainMaterialized full pathMaterializedFast reads; bulk update for renames; sync journal records one event
Large file resumeRestart from beginningResumable block-levelBlock-level resumeIndividual 4 MB blocks retry independently; progress preserved on disconnect
On-demand filesAlways download everythingSmart Sync (placeholders)Smart Sync95% of files not actively used; saves 100+ GB of local disk per user
Metadata consistencyEventualStrong per-namespaceStrongFile tree must be consistent; cursor-based sync depends on ordered journal
12

Reliability & fault tolerance

Phase 4 of 4 · Operations & resilience

Add operational safeguards

With the request paths and tradeoffs established, add the controls that help detect trouble and keep the system operating. The final diagram retains every layer introduced so far.

Conflict + recovery
Preserve conflicting versions and recover interrupted uploads without discarding either user's work.
Architecture after phase 4. Includes Sync client, Sync API, Metadata service, Block service, Metadata database, Object storage, Notification service, Conflict + recovery. Sync client connects to Sync API. Sync API connects to Metadata service. Sync API connects to Block service. Metadata service connects to Metadata database. Block service connects to Object storage. Metadata service connects to Notification service. Metadata database connects to Conflict + recovery.
Phase 4 — Added Conflict + recovery. Earlier layers remain in place.SVGSVGEditable diagramEditable diagram

Reliability & Fault Tolerance

Single Points of Failure & Mitigations

Single Points of Failure & Mitigations
Single Points of Failure & MitigationsSVGSVGEditable diagramEditable diagram

Graceful Degradation

EXAMPLE
Tier 1 (Notification service down):
  → Clients fall back to polling every 60 seconds
  → Sync works, just 0-60s delay instead of near-instant
  → Core functionality unaffected

Tier 2 (Search index down):
  → File search unavailable
  → Browse by folder, upload/download, sync all work
  → Rebuild index from metadata DB

Tier 3 (One metadata shard down):
  → Users on that shard: files inaccessible briefly
  → Auto-failover to standby (< 30s)
  → Other users on other shards: unaffected

Tier 4 (Block store partially degraded):
  → Reads served from surviving replicas
  → Uploads to degraded AZ rerouted
  → Background repair restores full replication

Tier 5 (Client offline):
  → Full offline access to previously synced files
  → Changes queued in local SQLite DB
  → When online: changes sync automatically
  → This IS the normal mode for mobile devices
13

Production architecture

High-Level Architecture

High-Level Architecture
High-Level ArchitectureSVGSVGEditable diagramEditable diagram

Component Breakdown

Component Breakdown
Component BreakdownSVGSVGEditable diagramEditable diagram

Full System Architecture (Production-Grade)

Full System Architecture (Production-Grade)
Full System Architecture (Production-Grade)Wide diagram — scroll horizontally.SVGSVGEditable diagramEditable diagram
14

Further exploration

Rename a folder on one offline device while editing a file inside it on another. Stable file identities help distinguish a move from deletion followed by creation. Work through the merged folder tree, then add a genuine deletion and decide how long tombstones must survive for old devices to catch up safely.

15

Interview playbook

Interview Tips

  1. Start with chunking — CDC is the key insight — "Files are split into 4 MB content-defined chunks using Rabin fingerprinting. Boundaries are determined by content patterns, not fixed offsets. This means inserting a byte only changes one chunk, not all subsequent chunks." This single decision enables delta sync, dedup, and bandwidth efficiency.

  2. Deduplication saves 30-50% storage — Content-addressable blocks: same SHA-256 hash = same block, stored once. Works across file versions (edit changes 1 of 4 blocks → 75% reuse) and across users (10K users with same installer = stored once). This is how Dropbox stores 100B files in ~35 PB.

  3. Journal-based sync with cursor — Don't describe "compare all files." Explain: server maintains an append-only change journal per namespace. Client stores a cursor (last_change_id). GET changes?cursor=N returns only what's new. O(changes) not O(total_files). This is how incremental sync stays fast.

  4. Upload dedup flow — Client sends list of block hashes. Server checks dedup index. Returns: "I need only blocks X and Z (Y already exists)." Client uploads directly to object storage via presigned URLs. API server never proxies file content (only metadata).

  5. Conflict resolution = conflict copies — Don't say "last-writer-wins." Explain: if two devices edit the same file offline, server detects base-version mismatch. Creates "file (conflicted copy - Device B).txt." No silent data loss. User decides which version to keep. Version history preserves everything.

  6. Push notification + polling fallback for sync — Changes push to other devices via long-poll/WebSocket (< 1s). If push fails, client polls every 60 seconds. Offline devices sync on reconnect using their stored cursor. Three-layer reliability.

  7. Smart Sync / on-demand files — "User has 500 GB in cloud, 256 GB local disk. Solution: virtual filesystem shows all files but downloads content only on open. Cloud-only files have metadata locally but zero disk usage. Evict least-recently-used files when disk is low." This is a modern differentiator.

  8. Resumable upload at block level — 10 GB file = 2,500 blocks. Each block uploaded independently. Connection drops → resume from exactly where you stopped. Server tracks which blocks received. No re-uploading completed blocks.

  9. Block storage with tiered retention — Hot (3× replication, SSD cache) for recent files, cold (erasure coding RS(6,3)) for older files, archive for version history. Lifecycle migration based on access patterns. Same model as S3 storage classes.

  10. End with scale numbers — "100B files chunked into ~400B blocks, deduplicated to ~35 PB of unique data. CDC saves 70% of upload bandwidth. Cursor-based sync handles 100K metadata ops/sec. Push notification propagates changes to other devices in < 2 seconds."