Back to System Design
System Design
hard
mid

How would you design a photo sharing app like Instagram?

Photo-first social product: feed (see [[design-an-instagram-like-feed-page]]) + upload flow + profile + explore. Upload: client crops/filters → direct-to-S3 pre-signed URL → post-process server-side → create post referencing uploaded media. Heavy image optimization (responsive srcset, modern formats, blurhash). Stories (24h ephemeral), DMs, search/explore powered by server.

6 min read·~35 min to think through

Instagram is feed + upload + profile + explore + stories + DMs. The interview wants the architecture across all surfaces, plus a deep dive on the photo upload pipeline (which is distinctive).

1. Surfaces

  • Feed — see [[design-an-instagram-like-feed-page]] for the full treatment.
  • Upload / Create — pick → crop → filter → share.
  • Profile — grid of user's posts, follow stats, bio.
  • Explore — algorithmic discovery (server-driven).
  • Stories — 24h ephemeral carousel.
  • DMs — see [[frontend-system-design-design-a-chat-app-messenger]].
  • Reels / video — see [[frontend-system-design-design-video-streaming-netflix]] patterns.

2. Upload pipeline (the distinctive one)

ts
Pick → Crop → Filter (client-side preview) → Upload → Post-process → Publish

Client-side editing

  • File pick via <input type="file"> (camera or library).
  • Crop in a canvas; output a normalized image.
  • Filters applied via CSS filter for preview; the final image can be either:
  • Server-applied (send original + filter id; server applies for fidelity).
  • Client-applied via canvas (faster, but client decides quality).
  • Compression — resize to a max dimension before upload (e.g., 1080px wide).

Direct-to-storage upload

Don't proxy bytes through your app server. Direct upload to S3 (or equivalent) via a pre-signed URL:

ts
1. Client → App: requestUploadUrl(mime, size)
2. App → Client: { uploadUrl, mediaId }
3. Client → S3: PUT uploadUrl with file bytes
4. Client → App: createPost(mediaId, caption, ...)

Pros: app server stays light; resumable; cheaper.

Resumable / chunked

For larger files (video), use multipart upload with resumability.

Progress + cancel

Show upload progress (XHR onprogress or fetch streams). Allow cancel mid-upload.

Post-processing

After upload, the server:

  • Validates and re-encodes.
  • Generates multiple sizes for srcset.
  • Generates AVIF/WebP variants.
  • Generates blurhash / LQIP for placeholders.
  • Runs moderation models.

3. Feed (recap)

See [[design-an-instagram-like-feed-page]]. Cursor-paginated, virtualized, optimistic likes, autoplay videos.

4. Profile grid

  • 3-column responsive grid of posts.
  • Lazy-load on scroll (IntersectionObserver).
  • Tap → post detail (route change, deep-linkable).

5. Explore

  • Server returns a personalized grid.
  • Mixed aspect ratios; masonry-ish layout.
  • Infinite scroll, virtualized.

6. Stories

  • 24h ephemeral — fetch only stories from followed users.
  • Carousel at the top of the feed.
  • Viewer: tap to advance, swipe between users, long-press to pause; auto-advance with progress bars per story.
  • Mark seen on view; persists.

7. DMs

  • Chat patterns from [[frontend-system-design-design-a-chat-app-messenger]].

8. Image optimization (dominant cost)

  • Pre-generated sizes on upload (200, 400, 800, 1080, 1440).
  • Responsive srcset with appropriate sizes.
  • AVIF/WebP with JPEG fallback.
  • Lazy-load below the fold.
  • Blurhash placeholders to avoid blank space.
  • Aspect-ratio reserved (Instagram crops to square / 4:5 / 16:9 — known at upload).

9. SEO

  • Profile pages and individual post pages SSR for crawlers and link previews.
  • Open Graph + Twitter Card metadata per post.

10. Offline / resilience

  • Service worker caches the feed shell.
  • Uploads queue in IndexedDB if offline; replay on reconnect.

11. Push notifications

  • Likes, comments, follows, DMs, story views.

Interview framing

"Instagram is feed + upload + profile + explore + stories + DMs — most patterns recur from a news feed. The distinctive piece is the photo upload pipeline: pick → client crop and filter preview → direct upload to S3 via pre-signed URL (don't proxy bytes through the app server) → server post-processes (resize, AVIF/WebP, blurhash, moderation) → createPost references the mediaId. Show progress, allow cancel, resumable for videos. The feed itself follows the news-feed playbook — cursor pagination, virtualization, optimistic likes — and stories are a separate ephemeral carousel with auto-advancing viewer. Dominant perf cost is media: pre-generated responsive sizes, modern formats, blurhash placeholders. SSR profile and post pages for SEO and link previews."

Follow-up questions

  • Walk through the photo upload pipeline.
  • Why direct-to-S3 instead of proxying through the app server?
  • How do you handle a failed upload mid-way?
  • How is the stories viewer different from feed video autoplay?

Common mistakes

  • Proxying upload bytes through the app server (cost, latency, scale).
  • No pre-generated responsive sizes — phone downloads 4000px images.
  • Stories without auto-advance and tap-to-pause.
  • Auto-scroll feed on new post arrival.

Performance considerations

  • Media dominates — pre-generated sizes, modern formats, blurhash, lazy below-the-fold. Direct upload offloads the app server.

Edge cases

  • Upload over flaky network (resumable).
  • Pre-signed URL expiry mid-upload.
  • User cancels mid-upload (cleanup orphan media).
  • Story expires while a user is viewing it.

Real-world examples

  • Instagram, Pinterest, Snapchat.

Senior engineer discussion

Seniors design the upload as the distinctive piece (direct-to-storage, post-process, progress + resumable), reuse feed/chat patterns for the recurring surfaces, and treat media optimization as the dominant lever.

Related questions