Design Instagram's photo upload, hybrid fan-out feed, and CDN delivery for 500M DAU, covering the media pipeline and petabyte-scale Cassandra storage.
TL;DR
- Keep binary upload, media processing, post metadata, feed generation, and photo delivery on separate paths.
- Let clients upload directly to durable object storage with a short-lived signed URL; process images asynchronously and publish a post-ready event only after the required renditions exist.
- Use a hybrid feed: fan out regular authors' post IDs into follower caches, while merging posts from very large accounts at read time. The threshold and cache sizes are illustrative policy settings.
- Serve media through a CDN with signed URLs and an origin shield. Hydrate feed IDs from a store shaped for point lookups, and use a separate access pattern for profile pages.
- The illustrative scenario uses 500 million daily active users, 100 million uploads per day, and a 200ms p99 feed target. These are requirements for the interview design, not facts about the real product or guarantees from a provider.
ο»Ώ---
Scope and assumptions
This article designs a photo-sharing system where users upload photos with captions, follow accounts, view profile grids, and read a reverse-chronological home feed. The design covers image processing, metadata storage, feed fan-out, object storage, CDN delivery, and the failure modes created by asynchronous pipelines.
The interview scenario uses these illustrative assumptions:
- About 2 billion registered users, 500 million daily active users, 100 million photo uploads per day, and a peak upload rate of about 3,500 per second.
- Active users load a feed around 10 times per day and receive 12 posts per load; the resulting feed-load and fan-out estimates are upper-bound planning inputs.
- Photos are write-once after processing, have thumbnail, medium, and full-size renditions, and may be public or access-controlled. Authorization must happen before issuing a signed URL for private content.
- The home feed is eventually consistent. A short delay in seeing a new post is acceptable, but a feed request should remain available when an asynchronous worker is delayed.
- Likes, comments, discovery, stories, video, and direct messages are out of scope. The numbers below are illustrative requirements, not production measurements, product facts, or provider guarantees.
Functional Requirements
Core Requirements
- Users can upload a photo with a caption.
- Users can follow and unfollow other users.
- Users can view their home feed: reverse-chronological photos from accounts they follow.
- Users can view a profile page: the photo grid of all posts by a specific user.
Below the Line (out of scope)
- Engagement features (likes, comments, reactions) and content discovery (Explore, search)
- Stories (24-hour ephemeral photos)
- Reels (short-form video)
- Direct messages
The hardest part in scope: Generating the home feed. Under the illustrative 500M-DAU workload, assembling it on demand by querying across all followed users is not viable. Pre-computing feeds and delivering cached photos through a CDN within the illustrative latency target are the main scaling decisions.
Engagement features (likes, comments, reactions, Explore, and search) are below the line because they do not change the upload or feed delivery paths. A later likes extension could store a post_likes table keyed by (post_id, user_id) and cache the count in Redis per post. Search would require a separate search index consuming post-creation events for full-text caption indexing.
Stories are below the line because they introduce a separate ephemeral storage lifecycle and a dedicated stories feed that does not interact with the home feed pipeline. A later extension could give story metadata a 24-hour lifecycle and reuse the object-storage and CDN path for media delivery.
Reels introduce video transcoding, converting the upload pipeline into a multi-step encoding job. A later extension could add a video transcoder and Adaptive Bitrate (ABR) manifest generation alongside the image resizing workers.
Direct messages require a separate real-time messaging system. A later extension could use WebSocket connections through a dedicated chat service backed by a message store, entirely separate from the feed and media systems.
Non-Functional Requirements
Core Requirements
- Availability: Illustrative target of 99.99% uptime. Prefer feed availability over perfect freshness: a feed missing the last 30 seconds of posts is acceptable; a failed feed load is not.
- Durability: Uploaded photos must not be silently dropped or corrupted. Use object storage with a documented durability target and verify the upload and processing state.
- Latency: Illustrative targets are home-feed loads under 200ms p99, cached photo delivery under 100ms for the intended geographies, and upload acknowledgement under 1 second.
- Scale: Illustrative scenario of 2B registered users, 500M DAU, and approximately 100M photos uploaded per day (about 1,160 uploads per second on average, peaking at about 3,500 per second during an event).
- Read throughput: As an illustrative workload, each active user loads a feed around 10 times per day, or 5B feed loads per day (about 58K per second on average and about 175K per second at the assumed peak). Each load fetches 12 photos.
Below the Line
- Sub-10ms photo delivery via CDN edge-node pre-warming
- Real-time like-count consistency in feed
Read/write ratio: In this illustrative workload, 100M uploads per day versus 5B feed loads per day gives roughly 50 feed loads per upload; multiplying by 12 returned posts gives an upper-bound of about 600 post placements viewed per upload. With an illustrative average of 300 follows, each regular-author upload can trigger up to 300 feed-cache writes, or up to 30B placements for 100M uploads per day before inactive-user and influencer optimizations. This fan-out multiplier, not the raw upload rate alone, drives the infrastructure decisions.
The illustrative 200ms p99 feed target favors eventual consistency: a user missing the last 30 seconds of posts is a better outcome than a failed page load. That target rules out assembling the feed on the read path by querying the database for each followed user. The 1-second upload-acknowledgement budget requires decoupling media processing from the upload response, and the availability target requires redundancy in the hot read path.
30-second answer
Use a two-phase upload: create post metadata and a short-lived signed object-storage URL, then let the client upload the bytes directly. An asynchronous media pipeline creates the required renditions and emits PostPublishedEvent only when they are ready. Feed workers fan out regular authors' post IDs into per-user Redis sorted sets; posts from high-follower authors are merged at read time. The Feed Service hydrates IDs from a post store and returns signed CDN URLs. The feed is eventually consistent, while object storage and post metadata are durable sources of truth.
5-minute explanation
The upload path is deliberately small: Post Service validates metadata, creates a post ID, stores a pending post, and returns a signed upload URL. The client sends image bytes directly to object storage. A confirmation event enters a durable media queue; workers resize and compress the image, write each rendition, and update the post only after all required outputs are present.
Once media is ready, a post-ready event enters the feed pipeline. Regular authors are fanned out to active followers' sorted-set caches. Very large accounts are handled with a hybrid strategy: do not perform an enormous write fan-out, and merge their recent posts on feed reads using a short-lived per-author cache. Feed reads fetch IDs, batch-hydrate metadata, and sign CDN URLs; the image bytes come from the CDN rather than the application fleet.
The data model follows access patterns. A user-keyed post table serves profile pages, while a post-ID-keyed table serves feed hydration. The follow graph is stored in both directions so fan-out and feed reads do not require scans. All queues and workers are idempotent because delivery is at least once and cache entries can be replayed.
The system favors feed availability and bounded staleness over a globally synchronous timeline. Deletion, authorization, and media failure are explicit workflows: soft-delete metadata first, invalidate or version media access, remove feed entries asynchronously, and surface processing failures rather than silently dropping them.
Core entities
- Post: The core content entity. Carries a
post_id,user_id,caption,media_keys(the S3 object keys for each processed resolution),media_status, andcreated_at. Themedia_keysare populated asynchronously after processing completes. - User: An account with a profile,
follower_count, andfollowing_count. Thefollower_countfield drives the influencer threshold check in the fan-out strategy. - Follow: A directed edge from follower to followee. The follow graph is the input to every home feed generation and fan-out operation in the system.
- Feed (derived): A pre-computed ordered list of post IDs cached per user. Not a stored entity.
The full schema, index strategy, and partition keys are deferred to the deep dives. The four entities above are sufficient to drive the API design and high-level architecture.
API design
Use a two-phase upload rather than a multipart form POST to the app server so binary image bytes stay off the application fleet. The exact peak rate is an illustrative capacity input, but the separation remains useful as upload objects become large.
Upload a photo:
POST /posts
Body: { caption, media_type }
Response: { post_id, upload_url }
Acknowledge upload complete:
PUT /posts/{post_id}/media
Body: { upload_confirmed: true }
Response: 202 Accepted
Get home feed:
GET /feed/home
Query: { cursor?, limit? }
Response: { posts: [...], next_cursor }
Get profile posts:
GET /users/{user_id}/posts
Query: { cursor?, limit? }
Response: { posts: [...], next_cursor }
Follow a user:
POST /users/{user_id}/follows
Response: 201 Created
Unfollow a user:
DELETE /users/{user_id}/follows
Response: 204 No Content
Two-phase upload: Photo uploads use a two-phase pattern. The first
POST /postsgenerates a pre-signed S3 URL and a post_id without touching media storage. The client uploads directly to S3 using the signed URL. The secondPUT /posts/{id}/mediasignals the server that the upload is complete, triggering the async processing pipeline. This keeps large binary transfers off the application servers entirely.
Cursor pagination: All feed endpoints use cursor-based pagination rather than offset. A user's feed changes while they scroll as new posts arrive. Offset pagination skips or repeats posts when items are inserted at the top. A cursor encodes the last-seen post_id, and every subsequent page begins strictly after that ID.
45-minute interview approach
Use this section only to pace the design prompt; keep the media, feed, CDN, and storage mechanics in the architecture and deep dives.
- 0-5 minutes β clarify the product: Confirm photo-only scope, caption and follow behavior, public versus access-controlled posts, feed ordering, pagination, deletion expectations, and whether media processing may be asynchronous.
- 5-10 minutes β requirements and estimates: State the illustrative upload, feed-load, fan-out, latency, availability, and retention targets. Calculate average and peak upload rates and call out the follower fan-out multiplier.
- 10-15 minutes β entities and APIs: Identify Post, User, Follow, and derived Feed. Sketch two-phase upload, upload confirmation, home feed, profile posts, follow, and unfollow endpoints.
- 15-25 minutes β baseline architecture and flows: Draw direct upload to object storage, post metadata, media queue and workers, post-ready events, feed cache, post store, CDN, and the upload, profile, and feed read/write flows.
- 25-35 minutes β choose deep dives: Let the interviewer select media processing, feed fan-out, CDN delivery, or metadata storage. Compare the naive option with the hybrid or asynchronous design.
- 35-41 minutes β reliability, security, and operations: Cover at-least-once events, idempotent workers, partial media failure, deletion, signed URL authorization, cache/index lag, queue backpressure, and observability.
- 41-45 minutes β trade-offs and close: Explain eventual feed consistency, influencer read amplification, dual-write repair, CDN invalidation, retention costs, and the changes needed for video or engagement features.
High-level architecture and critical flows
The system has four critical flows: upload stores metadata and bytes, media processing creates renditions and publishes readiness, feed generation places post IDs into derived feeds, and feed/profile reads hydrate metadata and deliver signed media URLs. The write paths are asynchronous where possible; the read path is optimized for predictable latency.
1. Users can upload a photo
The write path: client requests a pre-signed URL, uploads image bytes directly to S3, then confirms the upload. The Post Service never touches the image bytes.
Components:
- Client: Mobile or web app initiating the two-phase upload flow.
- Post Service: Validates the request, generates a post_id, issues a pre-signed S3 URL, inserts the post row with
media_status = pending, and publishes aMediaUploadedEventon confirmation. - Object Storage (S3): Receives the raw binary upload directly from the client.
- Post DB: Stores the post metadata row. Media keys are populated asynchronously after processing.
Request walkthrough:
- Client sends
POST /postswith caption and media type. - Post Service generates a post_id and inserts
{ post_id, user_id, caption, media_status: "pending", created_at }into Post DB. - Post Service generates a pre-signed S3 URL valid for 5 minutes and returns
{ post_id, upload_url }. - Client uploads image bytes directly to S3 using the pre-signed URL.
- Client sends
PUT /posts/{post_id}/mediato confirm the upload is complete. - Post Service publishes
MediaUploadedEvent { post_id, s3_raw_key }to Kafka. - Post Service returns
202 Accepted.
The media processing pipeline that resizes and optimizes the uploaded image is deferred to deep dive 1. Only the upload and acknowledgment path is shown here.
2. Users can view a profile page
Treat the profile page as the simpler read case before tackling home-feed merging. Solving the single-user query first makes the harder fan-out discussion easier to follow. A database index on (user_id, post_id) is sufficient for this access pattern.
Components:
- Post Service (updated): Serves profile page reads. Queries the Post DB using the user_id plus a cursor.
- Post DB (updated): Index on
(user_id, post_id)enables efficient per-user queries. Since post_id encodes creation time (Snowflake; covered in deep dive 4), this index gives chronological order without a separate timestamp index.
Request walkthrough:
- Client sends
GET /users/{user_id}/posts?limit=12. - Post Service queries:
SELECT * FROM posts WHERE user_id = ? AND post_id < cursor ORDER BY post_id DESC LIMIT 12. - Post Service returns the post list with a cursor encoding the last post_id.
Profile is a single-user read. Home feed requires merging posts across all followed accounts, which is the next two requirements.
3. Users can follow and unfollow other users
The follow graph powers every home feed. It must answer two questions fast: who do I follow (for reading my home feed) and who follows me (for fan-out when I post). Both directions must be O(1) per lookup.
Components:
- Follow Service: Handles
POSTandDELETEon follow relationships. Writes both directions of the adjacency graph on every operation. - Follow Store: Keyed adjacency lists in both directions:
follower_id β [followee_ids]andfollowee_id β [follower_ids].
Request walkthrough:
- Client sends
POST /users/{followee_id}/follows. - Follow Service writes
(follower_id, followee_id)in the forward direction and(followee_id, follower_id)in the reverse direction into the Follow Store. - Follow Service returns 201 Created.
Maintaining both directions doubles the write cost on follow and unfollow. The payoff is O(1) reads for the two access patterns that run on every post write and every feed load. Computing one direction from the other at query time would require a full-table scan across billions of edges.
4. Users can view their home feed
This is the hard requirement. Home feed must merge posts from every followed account, sorted by recency, and meet the illustrative 200ms p99 target. A user following 500 accounts cannot trigger 500 database queries per feed load. The feed must be mostly pre-computed, with a bounded read-time merge for very large accounts.
Components:
- Feed Workers: Async workers consuming
PostPublishedEventevents from Kafka and writing post_ids into each follower's feed cache. These fire after media processing is complete. - Kafka: Durable message queue decoupling post publication from fan-out.
- Redis Feed Cache: Per-user sorted sets. Key:
home_feed:{user_id}. Score: post creation timestamp. Value: post_id. Capped at 800 entries per user. - Feed Service (new): Handles all home feed reads. Fetches post_ids from Redis and hydrates them into full post objects.
Request walkthrough (write path):
- Media processing completes (deep dive 1). The Media Worker publishes
PostPublishedEvent { post_id, author_id, created_at }to the post-ready Kafka topic. - Feed Worker reads the event and fetches the author's follower list from Follow Store.
- Feed Worker calls
ZADD home_feed:{follower_id} {timestamp} {post_id}for each follower and trims the sorted set to 800 entries.
Request walkthrough (read path):
- Client sends
GET /feed/home. - Feed Service calls
ZREVRANGE home_feed:{user_id} 0 11on Redis. - Feed Service batch-fetches full post objects for the returned post_ids.
- Feed Service returns the assembled feed.
This is the baseline: posts write through Kafka to pre-computed Redis feeds; home feed reads serve entirely from Redis. The Fan-out Worker's naive loop over all followers collapses for accounts with millions of followers, which we address in deep dive 2.
The Feed Worker as shown iterates over every follower for every post. An account with 5 million followers triggers 5 million Redis writes from one post. That loop stalls the fan-out pipeline for every other post queued behind it. Deep dive 2 addresses this directly with a hybrid strategy.
Deep Dives
The upload rates, follower counts, cache sizes, object sizes, and latency values in these deep dives are illustrative inputs for the interview scenario. Validate them with representative media, feed, and regional delivery tests.
1. How does the media processing pipeline work?
Three constraints drive the design:
- Uploaded photos arrive in arbitrary formats and sizes. The app must serve multiple resolutions (thumbnail, standard, high-res) matched to device capability and network conditions.
- The upload experience must be fast. Users should not wait for processing to complete before seeing confirmation.
- Image resizing is CPU-intensive. Running it inline on the upload server would block request-handling capacity at peak upload rates.
2. How do we generate home feeds at scale?
Three constraints define this problem:
- Home feed loads must meet the illustrative 200ms p99 target.
- A post from an account with millions of followers must not stall fan-out for all other users.
- The write amplification from fan-out peaks at approximately 3,500 uploads/second times 300 average followers, equal to ~1.05M feed-cache writes per second at peak.
3. How do we serve photos to 500M users under 100ms?
Three constraints drive this:
- Photos are large binaries (50KB to 5MB). Serving from a single origin region adds 150 to 400ms of round-trip latency for users far from the origin.
- Popular photos may be requested millions of times per hour. Fetching each from S3 on every request is expensive and slow.
- Photo URLs must not be guessable. A user should not be able to access another account's private photo by constructing a URL.
4. How do we store post metadata at scale?
Context: At the illustrative 100M posts per day, the post table would accumulate over 36B rows per year. A single relational database may not be the right operational shape for that dataset or for the two primary access patterns: profile page queries (user_id + cursor) and post hydration by post_id from the feed cache. The useful framing is "two access patterns, one dataset," which leads to a dual-access-pattern schema such as the Cassandra design below.
Security, reliability, and operations
- Keep raw and processed objects private by default. Authorize the viewer before signing a CDN URL, scope signatures to one object and rendition, and use short expiries for access-controlled media.
- Validate content type, size, image decoding, and metadata before handing files to workers. Isolate media processing from the request tier and make output writes idempotent so a replay cannot create inconsistent renditions.
- Give every queue event a stable ID and use an outbox or durable event record for the transition from
media_status = readyto feed publication. Dead-letter repeatedly failing events and expose them to operators. - Monitor upload-confirmation failures, media queue age, worker retries, rendition completeness, feed fan-out lag, feed-cache memory, post-store latency, CDN hit and error rates, and deletion propagation. Backpressure fan-out rather than allowing an influencer event to starve all other posts.
- Deletion should be a visible state transition: mark the post deleted, stop issuing new signed URLs, remove it from feeds asynchronously, invalidate or version CDN objects as needed, and delete originals according to the retention policy.
- Protect the follow graph and post metadata with service-level authorization, encryption in transit and at rest where required, audit access to private content, and a recovery plan for the post-store and object store.
Final Architecture
The media pipeline and feed pipeline run on separate topics and fire in sequence: a photo upload triggers media processing, and fan-out workers only fire after media_status = ready. This ordering is designed to prevent feed entries from pointing to renditions that have not finished processing; clients still need a fallback for a delayed or failed media object.
Interview Cheat Sheet
- Start by stating the two parallel async pipelines: one for media processing (raw upload to resized object-storage photos) and one for feed fan-out (post IDs into follower caches). They connect via the
PostPublishedEventpublished only after media is ready. - The upload acknowledgement stays within the illustrative sub-second target because the app server never handles image bytes. The client uploads directly to object storage via a pre-signed URL; the server validates, inserts metadata, and publishes an event.
- Media workers are stateless and horizontally scalable. Adding workers increases processing throughput without touching the upload request path. Adding a new output resolution is a config change, not a code deployment.
- State the read/write asymmetry early: the illustrative 100M uploads per day versus 5B feed loads per day fetching 12 photos each. The feed read path is roughly 50 times the upload count before counting returned posts.
- Fan-out on read breaks above roughly 100 followed accounts per user. Fan-out on write breaks for influencers above roughly 50K followers. The hybrid strategy splits cleanly at the influencer threshold and eliminates both failure modes.
- At 500M DAU, storing 800 pre-computed post IDs per user at a notional 8 bytes each is about 3.2TB of raw ID bytes before Redis and key overhead. Use that as a lower-bound sizing input, then measure the actual cache layout.
- Skip fan-out writes for inactive users. A Redis key
last_active:{user_id}set on each login with a 30-day TTL is all the gate logic needed. Reconstruct the inactive user's feed from the Follow Store and Post DB on their next login. - Serve photos through a CDN with signed URLs, not directly from S3. Signed URLs have a 1-hour TTL; deleted posts stop being accessible within one TTL cycle without invalidating every URL ever issued.
- A CDN origin shield can collapse many edge-PoP cache misses into fewer origin fetches. The actual number of origin reads depends on cache keys, eviction, TTL, request collapsing, and provider topology; validate it with a load test.
- Use Cassandra with two tables (posts_by_user keyed by user_id and posts_by_id keyed by post_id) to serve both access patterns: profile page queries and feed hydration. Dual-write on every post creation.
- Use a local-quorum write level when the chosen replication and failure model requires it, and a lower-latency read level for feed hydration only when eventual consistency is acceptable. Validate the choice against the store's replication behavior.
- Snowflake IDs for post_id make ORDER BY post_id DESC equivalent to ORDER BY created_at DESC. No secondary timestamp index is needed for chronological profile or feed queries.
- Fan-out ordering: never publish
PostPublishedEventbeforemedia_status = ready. The read path should also tolerate a CDN miss or a worker failure instead of assuming readiness is permanent. - Sign CDN URLs at API response time, not at upload time. Revoking access to a deleted post requires waiting at most one 1-hour TTL cycle, with no impact on any other URL ever issued.
- Photos are usually write-once after upload. Versioned object keys and a long cache lifetime can improve cache efficiency, but deletion and privacy requirements may require invalidation or a new versioned key.
- Choose a CDN and object-storage pairing after checking signed URL support, origin-transfer pricing, cache invalidation behavior, and access-control integration for the selected provider.
- Benchmark image libraries such as libvips and ImageMagick on the actual formats and sizes. CPU time and memory use directly affect the worker fleet; fixed speedup claims should not be assumed.
- Parallel resize (for example, one worker task per resolution) can reduce wall-clock time compared with sequential work, but the gain depends on CPU, memory, and object-storage limits. Media workers should be CPU-bound and horizontally scaled, not waiting on the request path.
- Soft-delete before hard-delete: set
status = deletedin the DB first, stop issuing new URLs, request CDN invalidation according to the provider's propagation behavior, then enqueue object deletion with a short delay. This avoids making metadata and cache state disagree during propagation. - If the illustrative upload volume and average object size produce hundreds of terabytes per day, lifecycle transitions for older originals may reduce storage cost. Model retrieval latency, minimum-storage-duration charges, deletion requirements, and user access patterns before moving data to colder tiers.
Trade-offs and alternatives
- Fan-out on read vs. fan-out on write: Read-time assembly keeps writes cheap but makes feed latency grow with follow count. Write-time fan-out makes reads predictable but amplifies writes for high-follower authors and inactive users. The hybrid threshold is a tunable policy, not a universal number.
- Pre-computed feed cache vs. source-of-truth reads: Redis sorted sets make the hot read fast but are derived state that can be lost or stale. Rebuild or backfill from the follow graph and post store; do not make cache loss equivalent to post loss.
- Direct object delivery vs. CDN: Direct delivery is simpler but pays origin latency and transfer cost on every miss. A CDN improves locality and absorbs popular reads, at the cost of signed URL management, invalidation, and cache consistency on deletion.
- Dual-write post schema vs. one query shape: Two access-pattern tables avoid scatter-gather for profile pages and feed hydration, but they introduce a consistency window and repair work. A relational store can simplify writes at smaller scale; sharding or a wide-column store becomes attractive when access patterns and volume demand it.
- Async media processing vs. inline processing: Async workers keep upload acknowledgement responsive and isolate CPU work, but users see a processing state and operators must handle queue lag and partial renditions.
- Redis influencer merge vs. larger push fan-out: Reading large-account posts avoids millions of writes per post, but adds bounded read amplification for users who follow those accounts. Cache recent influencer post IDs and cap the merge work.
Follow-up questions
What happens if a media worker dies halfway through a resize? The event remains retryable, object writes are idempotent, and the post stays in processing until all required renditions exist. After repeated failures, send the event to a dead-letter queue and alert an operator.
How do you keep an inactive user's feed fresh? Skip expensive push writes while the user is inactive, then rebuild or merge the feed from recent posts and the follow graph when the user returns. Bound the rebuild work and cache the result.
How do you handle an author with millions of followers? Classify the author above a configurable threshold, skip push fan-out, and merge recent posts at read time. Cache that author's recent post IDs and protect the read path with a per-request work limit.
What if the feed cache is lost? Reconstruct derived post IDs from the follow graph and post history, or serve a degraded read-time feed while backfill runs. The canonical post metadata and media objects remain separate from the cache.
How are deleted or private photos protected? Mark the post inaccessible before issuing new URLs, check authorization before signing, and asynchronously remove feed entries and invalidate or version cached objects. A cached URL can outlive metadata, so the exposure window must be part of the policy.
What changes for video or stories? Video adds transcoding and bitrate manifests; stories add a short-lived metadata lifecycle and a separate feed. Reuse object storage and CDN primitives only where their retention and access semantics still fit.
Common mistakes
- Proxying large image bytes through the application servers or waiting synchronously for every rendition before acknowledging an upload.
- Publishing a feed entry before
media_status = ready, or failing to handle a missing rendition and CDN miss on the read path. - Using fan-out on read for every followed account, or pure fan-out on write for high-follower authors and inactive users.
- Treating a Redis feed cache as canonical post storage, with no rebuild, TTL, memory, or backpressure plan.
- Using offset pagination for a changing feed, which can repeat or skip posts as new items arrive.
- Assuming a CDN URL is private because its path is hard to guess, or issuing signed URLs before authorization.
- Performing non-idempotent dual writes and having no outbox, repair consumer, or deletion path.
- Presenting upload rates, follower counts, latency, cache sizes, or storage volumes as real product facts or provider guarantees rather than illustrative requirements.
Test Your Understanding
1. Why upload directly to object storage? It keeps large binary transfers and CPU-heavy processing off the request fleet. The API can acknowledge metadata and queue work while the client uploads through a scoped URL.
2. Why is the feed strategy hybrid? Read fan-out scales poorly with follow count; write fan-out scales poorly with follower count. The hybrid places each cost where it is bounded and accepts a small read merge for very large authors.
3. What is the source of truth for a feed entry? A feed cache is derived state. Post metadata, the follow graph, and the media objects are authoritative enough to rebuild it; cache loss should not delete content.
4. Why can a ready post still fail to display? The CDN may miss or an object may be unavailable, so readiness is a pipeline state, not a guarantee that every delivery succeeds. The client and Feed Service need a fallback and observable error path.
5. What does at-least-once event delivery require? Stable event IDs and idempotent media, feed, deletion, and repair consumers. Reprocessing should converge to the same derived state.
Recap
Separate the byte path from the metadata path, and separate upload from media processing. Publish only ready posts into a hybrid feed pipeline, hydrate IDs from stores shaped for the required queries, and deliver media through an authorized CDN. Keep derived caches rebuildable, make every asynchronous consumer idempotent, and treat deletion, privacy, backpressure, and queue lag as first-class operational concerns.
Related concepts
- Caching β derived state, TTLs, invalidation, and cache failure modes.
- CDN β edge delivery, origin shielding, and cache behavior.
- Message queues β durable asynchronous pipelines and delivery semantics.
- Replication β copies, lag, and recovery for post and media stores.
- Scalability β partitioning, fan-out, and independent service scaling.