システム設計面接: 元Meta スタッフエンジニアと一緒にDropboxやGoogle Driveを設計する
Tony Duong
6月 11, 2026 ・ 2 分

Hello Interviewのシステム設計シリーズのもう一つのエントリで、Evan(元Metaのスタッフエンジニア兼面接官で、この問題を約50回出題してきた人物)が進行役を務めます。Design Dropbox(Design Google Driveとして出題されることもあります)はGoogle、Amazon、Metaで人気の問題です。難易度はやや易しめで、ミドルレベル(E4/L4)の候補者に最も多く出題されますが、シニアやスタッフにも出題され、その場合はdeep diveがレベルを分ける要素になります。
繰り返し使えるロードマップ: 要件 → コアエンティティ → API → 高レベル設計 → deep dive。
要件
- 機能要件: ファイルをアップロードする(リモートストレージへ)、ファイルをダウンロードする、そしてデバイス間でファイルを自動的に同期する(ローカルフォルダがリモートを双方向にミラーリングする)。スコープ外: 独自のblobストレージを自作すること — 「Design S3」は別の問題です。
- 非機能要件: 一貫性よりも可用性を優先する(CAP) — ドイツで変更されたファイルの古いバージョンを米国の誰かが一時的に見ても問題ありません。低レイテンシのアップロード/ダウンロード。最大50GBの大きなファイルを再開可能なアップロードでサポートする。そして高いデータ整合性(eventual consistencyは許容するが、いったん落ち着いたらローカルとリモートは一致していなければならない)。
教育的な余談: ここでは最初にback-of-the-envelope(概算)見積もりをしないこと。数字が設計を直接変える場合にのみ見積もる — そして、ほぼ無限にスケールするblobストレージがあれば、たいていの場合は設計を変えません。
コアエンティティ
- File — 生のバイト列で、blobストレージ(S3)に保存される。
- FileMetadata — file ID、名前、MIMEタイプ、サイズ、owner ID(userへのFK)、そしてバイト列へ戻るS3リンク。
- User — 最も重要度が低い。早い段階では省いた方がよい場合があり、ときに気を散らす要因になる。
API
POST /files— bodyはファイル + メタデータ。200を返す。GET /files/{fileId}— ファイル + メタデータを返す。GET /changes?since={timestamp}— 変更されたfile IDのリストを返す(後に、ラウンドトリップを節約するためにフルのメタデータを返すようにする)。
User IDはヘッダー(JWT / セッショントークン)に乗せ、bodyには入れません。Evanは最初に、これらのエンドポイントが**意図的に「間違っている」**ことを指摘します — 実際のアップロード経路はdeep diveの中で明らかになり、後で彼がそれらを修正しに戻ってきます。
高レベル設計
クライアント → ロードバランサー / API gateway(認証、レートリミット、SSLターミネーション、ルーティング)→ File Service。File Serviceはバイト列をblobストレージ(S3)に、メタデータをFile Metadata DBに書き込みます。メタデータの行がS3リンクを保持します。
- アップロード: ファイル → File Service → S3、その後メタデータを書き込み、200を返す。
- ダウンロード: file IDでメタデータを引き、S3リンクを取得し、S3から直接ダウンロードする(サーバー経由でバイト列をプロキシして戻さない)。
同期 — 興味深い要件
ほとんどの設計と違い、ここではクライアントが「fat(重い)」であり、モデル化する価値があります。クライアントはローカルフォルダ、クライアントアプリ、そしてローカルDB(何がすでにダウンロード済みかを知るためのメタデータ + fingerprint)を持ちます。方向は2つ:
- リモートが変更された → クライアントは定期的に
GET /changesをポーリングし、新規/変更されたファイルをダウンロードする。 - ローカルが変更された → OSがネイティブのファイル監視API(Windows: FileSystemWatcher、macOS: FSEvents)で通知する。アプリは通常の経路でアップロードし、メタデータを更新する。
Deep dive
deep diveは非機能要件を満たすために存在します。
大きなファイル(50GB) + 再開可能なアップロード
素朴な設計は約5〜10MBのファイルでしか機能しません。理由は2つ:
- 冗長なアップロード経路 — バイト列をFile Serviceへアップロードし、その後 S3へアップロードするのは帯域とCPUの無駄。
- リクエストbodyのサイズ制限 — ブラウザ/サーバー/gatewayはbodyサイズに上限を設けている(AWS API Gatewayは約10MB)ため、50GBのファイルはそもそも通せない。
修正1 — presigned URL。 メタデータのみをFile Serviceへ送り(status: started をセット)、次にS3からpresigned URLを要求します。S3はそのMIMEタイプとサイズに限定された、署名済みで有効期限付きのリンクを返します。クライアントはそれを使ってバイト列をS3へ直接アップロードします。
修正2 — chunking。 50GBのファイルは約100 Mbpsで約1時間12分かかるため、失敗時にゼロからやり直させてはいけません。クライアント側でファイルをchunk化し(約5MBのchunk)、chunkをS3へアップロードし(直列または並列)、各chunkのステータスをメタデータで追跡します。chunkを一意に識別するにはfingerprintingを使います — chunkのバイト列のハッシュがchunk IDになります。再開するには、クライアントのfingerprintを保存済みのchunkリストと比較し、欠けているものだけを再アップロードします。(DynamoDBでは chunks のリストとしてモデル化し、それぞれが { id: fingerprint, status, s3Link } を持ちます。)
chunkステータスを安全に更新する。 クライアントの「chunkをアップロードした」という主張を盲目的に信用しないこと — trust-but-verifyを使います。クライアントが成功を報告し、その後File Serviceが完了としてマークする前にS3に確認します。代替案: S3 notifications(change data capture)がサーバー側にイベントをpushする。なお、S3ネイティブのmultipart uploadがこれ(chunking、fingerprinting、検証)の多くを代わりにやってくれます。
低レイテンシなアップロード/ダウンロード
- chunkingがすでに役立つ — 適応的なchunkサイズでの並列chunkアップロードが、利用可能な帯域を最大限使い切る。
- CDN — 明らかな追加候補だが、追加する前に考えること: ユーザーはたいてい自分のファイルをダウンロードし、自分のデータセンターの近くにいるため、CDNが役立つことは稀で、コストもかかる。移動中のユーザーや非常に人気のある共有ファイルにのみ価値がある。
- 圧縮 — より少ないバイト数を送るが、選択的に行うこと: テキスト/DOCXはよく圧縮できる。すでに圧縮済みのメディア(JPEG/PNG/MP4)はほとんど縮まず、圧縮/解凍のコストに見合わない。ファイルタイプ + ネットワークに基づいてクライアント側で判断し、アルゴリズムをメタデータに記録する。
高いデータ整合性 / 同期の正確性
目標は2つ — 速いことと一貫していること:
- 速い: adaptive polling(適応的ポーリング)(アプリが開いている / アクティブなときはより頻繁にポーリングする)が、WebSocketやlong-pollingに勝る。後者は「数秒以内の更新」にはオーバースペック。さらにdelta sync — ファイル全体ではなく、変更されたchunkだけを取得し、クライアント側で再結合させる。
- 一貫している: 変更を検出するための選択肢は2つ —
- DBを直接ポーリングする(「最後の同期以降にchunkが変更されたフォルダX内のファイルをくれ」) — シンプルで、Evanが選ぶ方法。
- カーソル付きのイベントバス(例: Kafka) — 各変更がイベントになる。フォルダごとの同期カーソルが最後に読んだイベントを示す。これはDropboxが実際に行っていることに近く、監査証跡 / バージョニング / ロールバックを可能にするが、それらの要件がなければオーバースペック。
- reconciliation(突合): 最善を尽くしても、ローカルとリモートはずれることがあるため、定期的に(毎日/毎週)クライアントがリモートの状態を取得し、fingerprintを比較し、不整合を修正する。
最後に、彼はAPIの修正に戻ります: POST /files はメタデータを送り、presigned URLを受け取る。クライアントはそのURLへchunkをアップロードし、その後chunkステータスをpatchする — deep diveで明らかになった内容と一致します。
重要なポイント
- バイト列とメタデータを分離する — File Service経由で調整し、バイト列はpresigned URLでS3と直接やり取りする。
- 大きなファイルはchunk + fingerprint — 再開可能なアップロード、並列性、整合性、そしてdelta syncの基盤。
- chunkステータスはtrust but verify(またはS3 notificationsを使う)。S3 multipart uploadがこの多くをネイティブにやってくれる。
- 同期 = adaptive polling + delta sync、そして安全網としてのreconciliation。WebSocketはオーバースペック。
- CDNを疑うこと、そして選択的に圧縮すること — デフォルトが常に正しいとは限らず、その理由を述べることが評価につながる。
- 深さはレベルで正当化する: ミドルレベルは堅実な高レベル設計の後に止まってよい。シニア/スタッフは2〜3個のdeep diveを主導しなければならない。
🌐 Claudeによる翻訳