FSD Frontend System Design

Part VI β€” The Blueprint Β· Lesson 11.2

HLD – Photo Sharing App (Instagram)

In one lineAn image pipeline feeding an infinite feed β€” the two hard problems are upload and scroll.

Where you've seen itPosting a 4MB photo that appears almost immediately, then scrolling past a hundred images without a single frame drop.

  • Time20 min
  • DiagramUpload β†’ transcode β†’ CDN, plus the feed fetch path
  • Chapter11 Β· High Level Design

Diagram

diagram
1–2 Β· SCOPE AND SCALE

  in scope    feed Β· post detail Β· upload Β· like/comment Β· profile
  out         stories Β· reels Β· DMs Β· search Β· ads
  scale       500M DAU Β· ~30 posts/session Β· 4MB in β†’ 200KB out
              mobile web first Β· median device mid-tier Android
diagram
3 Β· ARCHITECTURE

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”   feed page   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ Client │◄─────────────►│   BFF   │───────►│ feed service β”‚
  β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜   cursor API  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚ post service β”‚
      β”‚                                        β”‚ user service β”‚
      β”‚ direct upload (presigned URL)          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
      β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”   trigger   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   write   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ Object │────────────►│ transcoder │──────────►│   CDN   β”‚
  β”‚ store  β”‚             β”‚ (variants) β”‚           β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                β”‚
                                                       β–Ό
                                         images served from the edge

  Upload bypasses the API server entirely β€” the browser PUTs
  straight to object storage with a short-lived presigned URL.
diagram
6a Β· DEEP DIVE β€” UPLOAD, and why it feels instant

  select file
      β”‚
      β”œβ”€β–Ί client-side downscale on a canvas (4MB β†’ 400KB)
      β”‚       cuts upload time ~10Γ— on mobile data
      β”œβ”€β–Ί show LOCAL preview immediately (URL.createObjectURL)
      β”‚       optimistic post appears in the feed at once (6.9)
      β”œβ”€β–Ί PUT to presigned URL, chunked, resumable
      β”‚       progress bar from the upload progress event
      └─► server transcodes to variants: 320 / 640 / 1080 / webp+avif
              post flips from "uploading" to live

  Failure path: keep the file in IndexedDB (6.5), retry on reconnect.
diagram
6b Β· DEEP DIVE β€” FEED SCROLL AT 60fps

  cursor pagination (10.11)  no duplicates when new posts arrive
  virtualization (10.7)      constant DOM size, flat memory
  <img srcset> + sizes       right variant per device pixel ratio
  fetchpriority=high on #1   the LCP image (5.5)
  loading=lazy below fold    never on the first card
  aspect-ratio boxes         reserve space β†’ CLS β‰ˆ 0 (5.1)
  IntersectionObserver       prefetch next page 400px early

How it works

  1. Feed = cursor-paginated, normalized store (6.6) so a like updates the post everywhere at once.
  2. Images dominate everything β€” variants, modern formats, and correct sizes matter more than any JS optimisation.
  3. Upload is direct-to-storage with client-side downscaling and an optimistic local preview.
  4. Interactions (like, follow) are optimistic with rollback (6.9).
  5. Rendering: SSR or SSG the shell for LCP, hydrate the feed, stream below-the-fold content (5.6).

Trade-offs

ChoiceCost
Client-side downscaleCPU on device, slight quality loss
Direct-to-storage uploadPresigned URL plumbing, harder validation
Optimistic postingRollback UI when transcoding fails
Virtualized feedComplexity, harder Ctrl-F and SEO

Interview angle

"The feed janks after a minute of scrolling. Why?"

  • Unbounded DOM: every scrolled card is still mounted, so memory and layout cost grow linearly β€” virtualize.
  • Images: wrong variant for the device, no aspect-ratio box, decoding on the main thread β€” use srcset, reserved space, and decoding="async".
  • Then verify with a flame chart (5.4) rather than guessing; long tasks name the culprit.

Recap

  • Two hard problems: upload latency and scroll performance.
  • Downscale on the client, upload direct to storage, preview optimistically.
  • Cursor pagination + virtualization + correct image variants carry the feed.