Skip to content

Google Photos#

Problem statement (interviewer prompt)

Design Google Photos: backup photos + videos from phones, multi-resolution serving, ML-powered search (objects, faces, scenes, OCR), albums, memories, and shared albums. Handle 100B+ photos across 1B+ users, with face clustering done per-user-only.

flowchart LR
  C([Phone])
  UP[Upload]
  S3[(Originals)]
  TR[Thumbs / Resize]
  CDN
  ML([ML tagging: face / OCR / object])
  IDX[(Search index)]
  ALB[Albums / Memories]
  C --> UP --> S3 --> TR --> CDN
  S3 --> ML --> IDX
  C --> ALB --> IDX

    classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
    classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
    classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
    classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
    classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
    classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
    classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
    classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
    classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
    classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
    class C client;
    class UP,TR,ALB service;
    class S3,IDX datastore;
    class ML compute;
flowchart TB
  subgraph Client[Clients]
    PH([Phone])
    WEB([Web])
    DESK[Desktop sync]
  end

  subgraph Upload[Upload Pipeline]
    PRE[Pre-signed URL]
    CHUNK[Chunked + resumable upload]
    HASH[SHA-256 + perceptual hash]
    DEDUP([Dedup against user library])
    ENC[Encrypt at rest]
  end

  subgraph Storage[Storage Tiers]
    HOT[(Hot tier - SSD)]
    COLD[(Cold tier - HDD/Tape)]
    ORIG[(Originals)]
    DERIV[(Derivatives<br/>thumbs 320/720/1080/4K)]
    CDN[Photos CDN]
  end

  subgraph Process[Processing]
    THUMB[Thumbnailer]
    EXIF([EXIF parser<br/>date, GPS, camera])
    LIVE[Live photo / motion]
    HEVC([HEIC → JPEG/WebP transcoder])
    VIDEO([Video transcoder ladder])
  end

  subgraph ML[ML Tagging]
    FACE([Face clustering<br/>per-user-only])
    OBJ[Object detection<br/>cat, dog, beach, food]
    OCR([OCR + scene text])
    LANDMARK[Landmark recognition]
    SAFE([NSFW / CSAM scan])
    EMB([Image embeddings])
  end

  subgraph Search[Search]
    INV[(Inverted index<br/>per user)]
    NLQ["Natural language query<br/>beach 2019 to labels + date"]
    PEOPLE[People search]
  end

  subgraph Albums[Albums & Memories]
    ALB[Album service]
    MEM([Memories generator<br/>this day, smart highlights])
    SHARE[Shared albums]
    PARTNER([Partner sharing])
  end

  subgraph Sync[Sync & Backup]
    SYNCSVC([Sync engine])
    NOTIF[Realtime notify]
    OFFLINE[[Offline queue]]
  end

  subgraph Meta
    META[(Metadata DB)]
    AUDIT[(Audit log)]
    QUOTA[Quota / Plan]
  end

  Client --> PRE --> CHUNK --> ORIG
  CHUNK --> HASH --> DEDUP
  CHUNK --> ENC
  ORIG --> THUMB --> DERIV --> CDN
  ORIG --> EXIF --> META
  ORIG --> HEVC --> DERIV
  ORIG --> VIDEO --> DERIV
  ORIG --> ML
  ML --> EMB --> Search
  ML --> META
  Client --> Search
  Search --> INV
  Client --> Albums
  Albums --> META
  Sync --- Client
  Sync --- ORIG
  Sync --- META
  ORIG -. age out .-> COLD

    classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
    classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
    classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
    classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
    classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
    classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
    classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
    classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
    classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
    classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
    class PH,WEB,DEDUP,FACE,PARTNER client;
    class CDN edge;
    class DESK,PRE,CHUNK,HASH,ENC,THUMB,LIVE,OBJ,LANDMARK,NLQ,PEOPLE,ALB,SHARE,NOTIF,QUOTA service;
    class HOT,COLD,ORIG,DERIV,INV,META,AUDIT datastore;
    class OFFLINE queue;
    class EXIF,HEVC,VIDEO,OCR,SAFE,EMB,MEM,SYNCSVC compute;

Storage tiering#

  • Originals always retained (per quota).
  • Derivatives regenerated lazily if missing - cheaper than always storing every size × format.
  • Cold tier on long-term storage for old photos with low access prob.

Face clustering (privacy-aware)#

  • Faces detected and grouped only within the user's library - never cross-user.
  • User confirms or merges clusters; not used for cross-user identification.
  • Combine inverted index over labels (cat, beach) with structured filters (date, location).
  • Image embedding search: text query → image embedding via CLIP-like model → ANN.

Sharing#

  • Per-album share token (capability URL); access control on view.
  • Partner sharing: bi-directional auto-share of selected albums.

Capacity#

  • Many billions of photos; ~5 MB / photo avg ≈ exabytes total.
  • Dedup saves significant cost when users re-upload same files across devices.

Glossary & fundamentals#

Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.

Tag Concept What it is Page
HLD CDN edge caching for static assets cdn
HLD Search internals inverted index, BM25, embeddings, ANN search-internals
LLD REST API design verbs, statuses, pagination, errors rest-api-design

Quick reference#

Functional#

  • Backup photos + videos from phones / desktops.
  • Multi-resolution display + downloads.
  • ML-driven search (objects, faces, text, locations).
  • Albums, memories, partner sharing.
  • Live / motion photos, RAW handling.

Non-functional#

  • 11×9s durability.
  • p99 thumbnail load < 300 ms (CDN).
  • Search response < 500 ms.
  • 1B+ users; many EB of storage.

Capacity#

  • 100B+ photos. Avg original 4 MB; full ladder 6× = ~25 MB per item.
  • Hot tier holds last ~30 days; rest cold.
  • Search index per user ~MB, fits sharded easily.

Schema#

  • media(id, owner_id, type, ts, original_ref, ladders[], hash, exif)
  • albums(id, owner, [media_ids])
  • labels(media_id, label, score, source)
  • faces(user_id, face_id, embedding, cluster_id)

Trade-offs#

  • Original retention vs storage cost: cold-tier old + dedup.
  • Server-side ML vs client-side: cloud is much more capable; offload to keep client battery.
  • Cross-user dedup: huge savings but ownership/privacy hazards - Google avoids it for non-identical content.
  • Live photos = paired image + short video; transcode strategy diverges.

Refs#

  • Google Photos engineering posts (face clustering, search architecture).
  • Apple iCloud Photos talks (different storage approach).
  • Image dedup papers; CLIP / vision embeddings for search.
  • ByteByteGo "Design Google Photos".

FAQ#

How does Google Photos back up 100 billion photos across users?#

Originals land in distributed object storage while a parallel pipeline produces multiple thumbnail sizes. Metadata, EXIF, and ML labels go to a sharded index optimized for search and timeline scans.

How does face grouping work in Google Photos?#

Face embeddings are computed per photo and clustered per user only, so identities never cross account boundaries. The user can name a cluster, which becomes the label for future matches.

Object detection, scene classification, OCR for text in images, and face clustering produce vectors and labels. A search index lets queries like beach 2022 mom return ranked results.

How does Google Photos avoid duplicate uploads?#

Clients hash each photo before upload. If the hash already exists in the user's library, the upload is skipped. Block level dedup also kicks in when many users save the same shared image.

How are thumbnails generated and delivered?#

On upload, a transcoder produces a fixed set of resolutions for grid views, zoom, and shares. Thumbnails are CDN cached aggressively while originals live in colder storage tiers.