Design a Google Drive Mobile Client
Storing, syncing, and versioning files from a phone — where a 10 GB upload has to survive a dropped signal and a locked screen.
Storing, syncing, and versioning files from a phone — where a 10 GB upload has to survive a dropped signal and a locked screen.
Scope and API (§1–2) frame the problem; the architecture (§3) is the skeleton. The four deep dives are the substance: local storage (§4), the resumable upload that anchors the whole chapter (§5), version history and conflict handling (§6), and at-rest encryption for enterprise (§7). Anything marked [+] is context added beyond the source chapter; the rest is a rewrite. Diagrams are original.
One user, many devices#
This is a cloud-storage client — the mobile front end to something like Google Drive. The scoping decision that simplifies everything: design for a single user across multiple devices, not multi-user sharing. That removes permissions and collaboration, but keeps the genuinely hard mobile problems: big uploads over flaky networks, version history, and offline access.
Functional requirements#
- Upload and download files up to 10 GB, and cancel in-progress transfers.
- Create and delete folders.
- View and restore previous versions of a file.
- Resume interrupted uploads where they left off.
- Sync files across the user's devices.
Non-functional requirements#
- Scalability — ~10 million daily active users, large data volumes, varied networks and devices.
- Data durability — reliable access to downloaded files even on poor connectivity.
Real-time collaboration (multi-user editing of Docs/Sheets) and sharing/permissions. Docs and Sheets are treated as ordinary files.
Files List (the hub for browsing files and folders), the Upload Modal, and the File Revisions screen (version history, with open/restore).
REST for actions, a push channel for changes#
Most operations are client-driven — upload, download, organize — so the core is HTTP + REST over JSON. But files change on other devices, and the client shouldn't poll to find out. For those lightweight "something changed" signals, REST is complemented by WebSockets or Server-Sent Events.
These notifications signal a change; they don't carry file bytes. So you don't need WebSocket's full bidirectionality — SSE (a one-way server→client stream over plain HTTP) is enough, and simpler. [+] WebSockets fit if you later need client→server messages on the same channel. [+] The chapter also notes gRPC + Protocol Buffers as a strong alternative at this scale — binary encoding, strong typing, efficient streaming — with the same patterns applying regardless of protocol.
Files and folders — unified#
A folder is just a file with a special type, so one set of endpoints manages both:
- GET
/v1/files?parentId&page&limit→GetFilesResponse(paginatedFilelist;parentIddefaults to root). - GET
/v1/files/{fileId}→ aFile. GET{downloadUrl}— download (URL is in theFile). - POST
/v1/files— create with metadata (name, mimeType, parentId). - DELETE
/v1/files/{fileId}PATCH/v1/files/{fileId}— delete / update metadata.
The File model carries id, name, mimeType,
size, an optional downloadUrl, and a checksum for integrity. All requests
use a JWT bearer token and JSON.
Revisions#
- GET
/v1/files/{fileId}/revisions— list. GET…/revisions/{revisionId}— details. - POST
…/revisions/{revisionId}/restore— revert to a version.
A FileRevision holds revisionId, fileId, mimeType,
size, modifiedAt, downloadUrl, and checksum — everything
needed to view and roll back.
The layered client#
Same layered split as the other case studies: a UI layer of screens and state holders over a data layer of repositories with local and remote sources, backed by a local Files DB and fronted by one Network Dispatcher.
External pieces: the Backend (REST + SSE/WebSockets), a CDN for static content, and the API Gateway (auth, routing, security). Later deep dives add upload-specific components.
Storing metadata and files on device#
The client holds two very different things locally, and they want different storage: small, highly-queried metadata, and large opaque file content.
Metadata → a relational database#
File/folder metadata (the File model) and version history (FileRevision) are queried
and updated constantly, and they have relationships — files belong to folders via parentId. That's
a natural fit for a relational database: fast structured queries, referential integrity, and it
scales as metadata grows. Each model maps to a table with its own DAO (Data Access Object).
Bound the metadata DB at a configurable size (e.g. 200 MB) with LRU eviction — recently accessed metadata stays, stale entries are cleared. Each record is tiny, so even a small cap holds a lot of entries.
Downloaded files → private sandboxed storage#
Files kept for offline use go in the app's private internal storage, sandboxed by iOS and Android, because it's protected from other apps, fully app-managed, and directly accessible (with an option to export to shared storage).
- Each file is named by its
fileId(e.g.…/files/downloads/<fileId>) to avoid collisions. - Before downloading, the app checks available space and warns if it's low — important when files can be 10 GB.
Box Drive lets users mark folders for offline access; selected content downloads in the background and is stored locally.
Uploads that survive interruptions#
Resumable uploads are the heart of a mobile cloud client. For a 10 GB file on a phone, restarting from zero because a signal dropped is unacceptable. Resumability lets users pause, recover from interruptions, and adapt to network or battery conditions.
Simple (small file, no metadata), multipart (small file + metadata in one request), and resumable (large files, or anywhere interruption is likely — which on mobile is everywhere). Google Drive recommends resumable for most cases: it works for small files too, at the cost of one extra HTTP request. This deep dive is about resumable.
The shape: init → upload → complete#
- Initiate — the client requests an upload session and gets a unique upload ID plus session details (chunk size, expiry).
- Upload chunks — the file goes up in chunks; if the connection drops, resume from the last successful chunk.
- Complete — after the final chunk, the server assembles the file with an optional integrity check.
Content-Range tells the
server where each chunk fits, and 308 vs 201 signals "more" vs "done."Endpoints (the multiple-endpoint style)#
Companies split this two ways: a single unified endpoint (X, Google — commands via headers/params) or multiple endpoints (Dropbox, Box — one per step). This design uses multiple:
- POST
/v1/upload?uploadType=resumable→UploadResponse(uploadId,totalChunks,chunksProcessed,chunkSize,uploadUrl,uploadExpiresAt). The request carries arequestIdidempotency key. [+] - PUT
/v1/upload/{uploadId}/chunkwithContent-Range→201if final,308 Resume Incompleteotherwise. [+] (308literally means "I've got what you sent, keep going.") - GET
/v1/upload/{uploadId}— session status, to resume after the next chunk pastchunksProcessed. If expired, start over. - DELETE
/v1/upload/{uploadId}— abort and free server resources.
Background tasks: the OS can kill a background upload anytime, so persist pending
UploadResponse state to disk and resume on next launch. Storage & permissions:
temp-cache files before upload, monitor free space, and use the system file picker (or request storage
permission, disabling upload if denied). Errors: on connectivity loss, save state and
resume from the last chunk; restart on an invalid/expired session; use exponential backoff
so retries don't hammer the server. Feedback: show progress from
chunksProcessed / totalChunks, including a background notification.
Architecturally this adds, in the UI layer, a File Picker and File Permissions Helper; in the data layer, a File Splitter (chunking), a File Uploader Repository, and a File Uploader Service that runs the upload in the background.
Versions and the conflicts they create#
Version history lets users revert to earlier states. Two questions decide the design: how do you upload a new version, and what happens when two devices edit at once?
Uploading a new version#
| Method | How | Trade-off |
|---|---|---|
| Full copy upload ✓ | Send the whole file on any change; server stores each version standalone. | Simple and reliable, but storage- and bandwidth-heavy for big files. |
| Block-level sync | Split into blocks, send only changed ones; server keeps a block registry and rebuilds versions. | Efficient for small edits to large files, but complex and resource-heavy on both ends. |
Most consumer drives — Google Drive, Box, OneDrive, iCloud — use full copy for its simplicity and reliability. Dropbox pioneered block-level sync (its Broccoli encoding and block-sync protocol) to save bandwidth.
Detecting conflicts: optimistic concurrency#
Optimistic concurrency control assumes conflicts are rare and only checks at write time. [+] Each file has a version identifier known to both sides; the client sends its current
version with the update (e.g. an If-Match: < version> header), and the server either accepts
it or rejects it.
It prevents lost updates and enables fine-grained resolution, at the cost of extra latency and occasional user-facing merge prompts — used by providers like Box (via HTTP version headers).
Resolving conflicts#
Consumer drives often favor eventual consistency: accept the update, resolve after. Two common strategies:
- Last-Write-Wins — the newest version silently becomes current (Dropbox's default).
- Version forking — keep both, renaming one (
filename (1).png). Google Drive prompts the user to replace or keep both.
Dropbox's API exposes both via strict_conflict and mode (add renames,
overwrite is last-write-wins), giving developers explicit control.
Encrypting files at rest#
A likely interview curveball: enterprise customers need on-device files encrypted. Without it, a lost or compromised device exposes files to anyone who can mount its storage.
AES-256 — industry standard, good balance of security and mobile performance — encrypts the metadata database (file names, folder structure) and downloaded files in private storage. Even with raw storage access, the data is unintelligible without the key.
Where the key lives#
Offline access means keys must live on-device, stored in hardware-backed secure storage:
- Android Keystore / iOS Keychain (with Secure Enclave) store keys in hardware, resisting extraction.
- Derive the key from the user's credentials with PBKDF2 [+] (a key-derivation function that stretches a password into a strong key), so only an authenticated user can unlock the data.
Performance: run encrypt/decrypt on background threads; expect slightly larger files (padding) and higher memory use. Memory: clear decrypted data on timeouts and low memory; watch for leaks. Errors: plan fallbacks for corrupted/inaccessible keys and prompt the user. Backups: device-tied keys mean encrypted files should be excluded from system backups (or re-encrypted with backup-specific keys). Device compatibility: check for secure hardware at startup and degrade gracefully on older devices, telling the user security is reduced.
Recap#
A mobile cloud-storage client is a study in making unreliable things resumable. The network drops, the OS kills your process, the device is lost — so every hard part here is about persisting just enough state to recover: chunk offsets to resume an upload, version identifiers to detect a conflict, a hardware-held key to unlock files after a cold start. The phone is assumed to fail; the design's job is to make failure survivable.
Further reading#
Every link below was fetched and confirmed live on 24 August 2026. One has been reorganized since the chapter and is flagged.
Resumable & chunked uploads
- Box — designing "Mark for Offline"How Box Drive downloads marked folders in the background for offline use — the §4 pattern in production.
- Google Photos — resumable uploadsGoogle's single-endpoint resumable protocol with upload commands and byte offsets.
- X (Twitter) — chunked media uploadThe INIT/APPEND/FINALIZE style referenced for single-endpoint uploads. Note: the X API docs were reorganized post-rebrand and this URL now redirects into the new developer platform. moved
- Box — chunked uploadsBox's multi-endpoint session approach (create/upload part/commit).
- Dropbox — upload_session APIThe canonical multi-endpoint example: start / append / finish.
- OneDrive — createUploadSessionMicrosoft Graph's resumable session with nextExpectedRanges and byte-range rules.
- Google Drive — manage uploadsThe simple / multipart / resumable upload types compared, straight from Drive's docs.
Versioning & sync
- PCWorld — Google Drive reviewConfirms Drive uploads whole files (no block-level copying) — the full-copy choice in practice.
- Block-level storageBackground for the block-level-sync alternative.
- Dropbox — Broccoli, syncing lessDropbox's engineering write-up on its block-sync encoding.
- Cloudwards — block-level file copyingA plain comparison of block-level vs full-copy sync.
- Optimistic concurrency controlThe detection technique behind §6 and the If-Match / 409 flow.
- Box — ensure consistencyBox's HTTP-header approach to optimistic concurrency.
Encryption & compression
- AESThe cipher used for at-rest encryption.
- Android KeystoreHardware-backed key storage on Android.
- iOS KeychainApple's secure key storage, integrating with the Secure Enclave.
- PBKDF2The key-derivation function that turns a password into a strong key.
- BrotliGoogle's lossless compression — the wrap-up's bandwidth-optimization extension.