Grokking the System Design Interview
Vote

0% completed

Designing Facebook’s Newsfeed

What is Facebook's Newsfeed?

Try it yourself

Sketch it here

Step 1: Clarify the Requirements

Step 2: Estimate the Scale

Step 3: Define the API

Step 4: Define the Data Model

Step 5: Draw the High-Level Design

Step 6: Go Deep

Generating the feed

How much feed to keep in memory

Publishing the feed

Ranking the feed

Partitioning the data

Step 7: Bottlenecks and Failure Points

Where AI Fits in This Design

Every case study in this chapter follows the same seven steps from System Design Interviews: A step by step guide, and builds its design from the components in the System Design Master Template. Keep both open beside you, and try each problem yourself before reading the solution.

What is Facebook's Newsfeed?

A Newsfeed is the constantly updating list of stories in the middle of Facebook's homepage. It includes status updates, photos, videos, links, app activity, and likes. These come from the people, pages, and groups that a user follows. Together they form a scrollable record of a user's own life and their friends' lives.

Every social network needs some form of newsfeed. Twitter, Instagram, and Facebook all show updates from friends and followed accounts. Designing Twitter and Designing Instagram only sketch the feed. This lesson designs it in full.

The hard part is timing. Do we build a user's feed when they ask for it, or before they ask? Push vs Pull Architecture introduces that trade-off. Here we apply it to a complete design.

Try it yourself

Before reading the solution, try designing it. Aim to produce four things: the requirements, a rough load estimate, the API, and a first drawing of the components.

Sketch it here

Step 1: Clarify the Requirements

💡 Always clarify requirements at the beginning of the interview. Ask questions until you know the exact scope of the system the interviewer has in mind.

We will design a newsfeed for Facebook with these requirements:

Functional requirements

  1. The newsfeed is generated from the posts of the people, pages, and groups that a user follows.
  2. A user may have many friends and follow a large number of pages and groups.
  3. Feed items may contain images, videos, or just text.
  4. New posts are appended to the newsfeed as they arrive, for all active users.

The fourth requirement makes the feed a live stream, not a static page. A new post must reach the feeds of active users without them reloading everything.

Non-functional requirements

  1. Real-time generation. The system should generate any user's newsfeed in real time. The maximum latency seen by the end user should be 2 seconds.
  2. Fresh posts. A new post should reach a user's feed within 5 seconds, assuming a new newsfeed request comes in.

These two numbers measure different things. The 2-second target starts when a user asks for their feed. The 5-second target starts earlier, when a post is created. It covers the write path. The post has to be picked up, ranked, and added to the follower's feed before that feed is served again.

Step 2: Estimate the Scale

Ask the interviewer for the base numbers first. Assume the average user has 300 friends and follows 200 pages.

Those 500 accounts are what a reader follows. They set the size of the feed we build for that reader. How many followers a poster has is a different number. It decides the cost of delivering that poster's posts, and step 6 depends on the difference.

Traffic. Assume 300 million daily active users, each fetching their timeline five times a day.

300M x 5 fetches                 = 1.5B newsfeed requests per day
1.5B / 86,400 seconds            = ~17,500 requests per second

Storage. Assume we keep about 500 posts of every user's feed in memory, for a quick fetch. Assume each post is about 1 KB.

500 posts x 1 KB                 = 500 KB per user
500 KB x 300M users              = 150 TB of memory
150 TB / 100 GB per server       = ~1,500 machines

Why is a post only 1 KB when feeds contain images and videos? The 1 KB is the post record: its text and its metadata. Photos and videos are stored separately, in blob storage. Blob storage is storage built for large binary files. The client fetches the media on its own, so the feed cache holds only the small post record.

Two of these numbers change the design:

  • 17,500 feed requests per second, each merging posts from about 500 accounts. That is too much work to do while the user waits. Feeds must be built ahead of time.
  • 150 TB of memory. The stored feeds do not fit on one machine. The feed cache must be partitioned across about 1,500 servers.

Step 3: Define the API

We can expose the service through REST APIs. The call that matters most is fetching the newsfeed:

getUserFeed(api_dev_key, user_id, since_id, count, max_id, exclude_replies)

Parameters:

  • api_dev_key (string): the API developer key of a registered account. We use it, among other things, to throttle users based on their allocated quota.
  • user_id (number): the ID of the user for whom the system will generate the newsfeed.
  • since_id (number): optional. Returns results with an ID higher than (that is, more recent than) the specified ID.
  • count (number): optional. The number of feed items to try to retrieve, up to a maximum of 200 per request.
  • max_id (number): optional. Returns results with an ID less than (that is, older than) or equal to the specified ID.
  • exclude_replies (boolean): optional. Prevents replies from appearing in the returned timeline.

Returns: (JSON) a JSON object containing a list of feed items.

The two ID parameters cover both directions of scrolling. since_id pages toward newer posts, and max_id pages toward older ones.

Step 4: Define the Data Model

There are three primary objects: User, Entity (a page or a group), and FeedItem (a post). The relationships between them are simple:

  • A User can follow other entities and can become friends with other users.
  • Both users and entities can post FeedItems. A FeedItem can contain text, images, or videos.
  • Each FeedItem has a UserID that points to the User who created it. For simplicity, we assume that only users create feed items. On Facebook, Pages can post feed items too.
  • Each FeedItem can optionally have an EntityID. It points to the page or group where the post was created.

If we use a relational database, we need to model two relations: User to Entity, and FeedItem to Media. Each user can be friends with many people and follow many entities. So we store the follow relation in a separate table, UserFollow. Its Type column records whether the followed ID is a User or an Entity. Similarly, a FeedMedia table links each FeedItem to its media. Each media file also gets a Media row, which holds the file's metadata.

One more record appears in the schema, but it is not a table. It is the pre-generated feed entry that the system keeps in memory, one per user. Step 2 already argued for it: feeds must be built ahead of time. The schema below shows the tables and that in-memory record:

Database schema for Facebook Newsfeed
Database schema for Facebook Newsfeed

User and Entity hold the profiles. A User row is one person: name, email, and a few dates. An Entity row is a page or a group, and its Type column records which of the two it is. Both tables grow only when new accounts appear, so they stay small.

The UserFollow table is the one to study. EntityOrFriendID can point at a user or at an entity, and Type says which one. The feed query in step 6 filters on that column. One row records one follow. The average reader from step 2 therefore adds about 500 rows here. With 300 million users, UserFollow outgrows every other table in the schema.

FeedItem is the post record that step 2 priced at about 1 KB: the text plus its metadata. UserID names the author, and the optional EntityID names the page or group it was posted in. NumLikes is a like counter. Ranking in step 6 reads it when it scores a post.

Media files are not in any of these tables. A Media row holds only the file's metadata and its Path, a reference to the file itself, which lives in blob storage. A FeedMedia row links one post to one media row. A post with three photos therefore has three FeedMedia rows.

The last record in the diagram lives in memory, not in the database. It is the pre-generated feed: one entry per user, holding that user's ready feed items in order. It is derived data: every item in it is a copy built from the tables above. If it is lost, the tables can rebuild it. Step 6 defines its exact structure, keyed by UserID, and shards it across the 1,500 cache servers from step 2.

Step 5: Draw the High-Level Design

At a high level, this problem divides into two parts: generating a feed and publishing it.

Feed generation. A newsfeed is generated from the posts of the users and entities that a user follows. Say we receive a request to generate the feed for a user named Jane. We perform these steps:

  1. Retrieve the IDs of all users and entities that Jane follows.
  2. Retrieve the latest, most popular, and most relevant posts for those IDs. These are the candidate posts for Jane's newsfeed.
  3. Rank these posts based on their relevance to Jane. This ranked list is Jane's current feed.
  4. Store this feed in the cache and return the top posts (say 20) to be rendered on Jane's screen.
  5. When Jane reaches the end of her current feed, the client fetches the next 20 posts from the server, and so on.

Notice that we generated the feed once and stored it in the cache. What about new posts from people Jane follows? If Jane is online, we need a way to rank those new posts and add them to her feed. The simple way is to rerun the steps above at short intervals, then notify Jane that newer items are waiting. A timer alone cannot meet the 5-second target from step 1, so step 6 adds a faster path for live updates.

Feed publishing. Whenever Jane loads her newsfeed page, she requests and pulls feed items from the server. When she reaches the end of her current feed, she pulls more. For newer items there are two options. The server can notify Jane, and she pulls. Or the server can push the new posts to her. Step 6 compares these options.

Components. At a high level, the Newsfeed service needs these parts:

  1. Web servers: maintain a connection with the user. This connection carries data between the user and the server.
  2. Application servers: run the workflow of storing new posts in the database servers. Other application servers retrieve the newsfeed and push it to the end user.
  3. Metadata database and cache: store the metadata about Users, Pages, and Groups.
  4. Posts database and cache: store metadata about posts and their contents.
  5. Video and photo storage, and cache: blob storage for all the media included in the posts.
  6. Newsfeed generation service: gathers and ranks all the relevant posts for a user, generates the newsfeed, and stores it in the cache. This service also receives live updates and adds newer feed items to any user's timeline.
  7. Feed notification service: notifies the user that newer items are available for their newsfeed.

The diagram below is the high-level architecture of the system. In it, User B and User C follow User A.

Facebook Newsfeed Architecture
Facebook Newsfeed Architecture

Read the diagram from the poster's side. User A creates a post, and the application servers store it in the posts database. Users B and C follow A, so the newsfeed generation service adds A's post to their stored feeds. The feed notification service then tells B and C that new items are waiting.

Step 6: Go Deep

Generating the feed

Take the simplest case first. The newsfeed generation service fetches the most recent posts from all the users and entities that Jane follows. The query would look like this:

(SELECT FeedItemID FROM FeedItem WHERE UserID in ( SELECT EntityOrFriendID FROM UserFollow WHERE UserID = <current_user_id> and type = 0(user)) ) UNION (SELECT FeedItemID FROM FeedItem WHERE EntityID in ( SELECT EntityOrFriendID FROM UserFollow WHERE UserID = <current_user_id> and type = 1(entity)) ) ORDER BY CreationDate DESC LIMIT 100

The two SELECT statements come from the two kinds of follow. The first finds posts by followed users, matching on UserID. The second finds posts in followed pages and groups, matching on EntityID. UNION merges the two result sets into one list, which is then sorted by creation date.

This design has four problems:

  1. It is very slow for users with a lot of friends and follows. We have to sort, merge, and rank a huge number of posts on demand.
  2. We generate the timeline when the user loads their page. That adds latency to every page load.
  3. For live updates, each new post causes feed updates for all followers. That could create high backlogs in the newsfeed generation service.
  4. For live updates, pushing (or notifying about) newer posts to users could create very heavy loads. That is especially true for people or pages that have a lot of followers.

The first two problems share a cause: we do the work while the user waits. To fix that, we pre-generate the timeline and store it in memory.

Offline generation. Dedicated servers continuously generate users' newsfeeds and store them in memory. When a user requests their feed, we serve it from the pre-generated, stored copy. The feed is compiled in the background on a regular basis, not on load.

Whenever these servers generate the feed for a user, they first check when that user's feed was last generated. Then they generate new feed data from that time onwards. We store this data in a hash table. The key is UserID. The value is a struct like this:

Struct { LinkedHashMap<FeedItemID, FeedItem> feedItems; DateTime lastGenerated; }

We store the FeedItemIDs in a structure like a LinkedHashMap or a TreeMap. Both keep the items in order and let us jump to any item by its ID. That combination is what pagination needs. When a user wants more feed items, the client sends the last FeedItemID it currently shows. We jump to that ID in the map and return the next batch of items from there.

How much feed to keep in memory

How many feed items should we store per user? Start with 500, then adjust from the usage pattern. Suppose one page of a feed shows 20 posts, and most users never browse more than ten pages. Then 200 posts per user covers almost everyone. A user who wants more posts than we hold in memory is served by a query to the backend servers.

20 posts per page x 10 pages     = 200 posts per user

Should we generate and keep feeds for all users? Many users do not log in often, and their pre-generated feeds waste memory. There are two options. 1) The simple option is an LRU cache. LRU (evict the least recently used entry) removes users who have not accessed their newsfeed for a long time. 2) A smarter solution learns each user's login pattern and pre-generates their feed just before they are likely to be active. Useful inputs are the time of day a user is active, and the days of the week they open their feed.

Publishing the feed

Publishing means getting a new post into the feeds of everyone who follows the poster. Pushing one post to all followers is called fan-out. So the push approach is called fan-out-on-write, and the pull approach is called fan-out-on-load.

Keep the two roles from step 2 apart here, because they are easy to confuse. The cost of fan-out-on-write depends on the poster. One post becomes one write per follower the poster has. The size of a feed depends on the reader. It is built from every account the reader follows. Our average reader follows about 500 accounts, which is manageable. But some posters have millions of followers, and that is where fan-out gets expensive.

1. Pull model, or fan-out-on-load. Keep all recent feed data in memory. Users pull it from the server whenever they need it. Clients can pull on a regular schedule, or manually. This has two problems. New data is not shown until the client issues a pull request. And it is hard to find the right pull interval. Most pulls return an empty response when there is no new data, which wastes resources.

2. Push model, or fan-out-on-write. Once a user publishes a post, we immediately push it to all their followers. The advantage is on the read side. When fetching a feed, we do not have to go through the reader's follow list and fetch posts for each account. That significantly reduces read operations. To receive pushes, each user maintains a long poll request with the server. Long polling means the client keeps a request open, and the server answers it only when new data exists. The problem is a poster with millions of followers (a celebrity user). One post means the server has to push updates to millions of people at once.

3. Hybrid. Combine fan-out-on-write and fan-out-on-load, and choose per poster. Stop pushing posts from users with a very high number of followers. Only push posts from users who have a few hundred (or thousand) followers. For celebrity users, let their followers pull the updates. Disabling fan-out for those few posters saves a huge number of resources. When a reader loads their feed, the system takes the stored list and merges in recent posts from the few celebrities they follow.

Two more variants are worth knowing. Once a user publishes a post, we can limit the fan-out to only their online friends. And we can combine "push to notify" with "pull for serving". The server pushes a small signal that new posts exist, and the client pulls the posts. A pure push or a pure pull model is less versatile than these combinations. Push vs Pull Architecture covers the general trade-off, and Designing Twitter applies it to a timeline.

How many feed items can we return per request? Set a maximum for one request. A typical page is 20 items, and the API caps count at 200. Within that limit, let the client specify how many it wants. A mobile client and a desktop client show different numbers of posts.

Should we always notify users about new posts? Notifying is useful. But on mobile devices data is relatively expensive, and pushed data can consume unnecessary bandwidth. So at least for mobile devices, we can choose not to push the data. Instead, users "Pull to Refresh" to get new posts.

Ranking the feed

The simplest ranking is by creation time. Modern ranking algorithms do a lot more, to make sure "important" posts rank higher. The idea is to select key signals that make a post important, then combine them into a ranking score. A signal is a feature of a post that we can measure.

Useful signals include the number of likes, comments, and shares, the time of the update, and whether the post has images or videos. A score computed from these features is enough for a simple ranking system. A better system evaluates itself constantly. It checks whether changes improve retention (how often users come back) and ad revenue.

Partitioning the data

Sharding posts and metadata. We have a huge number of new posts every day, and the read load is extremely high. So we distribute the data across many machines to read and write it efficiently. For the databases holding posts and their metadata, use the same sharding design as Designing Twitter.

Sharding feed data. The feed data lives in memory, and we partition it by UserID. We pass the UserID to a hash function, which maps the user to one cache server. That server holds all of the user's feed objects. We never expect to store more than 500 FeedItemIDs for one user, so a user's feed always fits on a single server. To get a user's feed, we query only one server. For future growth and replication, use consistent hashing. Consistent hashing means adding or removing a server moves only a small part of the data.

Step 7: Bottlenecks and Failure Points

The interviewer does not expect this design to be limitless. They expect you to know where it ends.

  • The read path depends on the feed cache. Every feed request goes to one cache server, found by hashing the UserID. If that server is down and not replicated, every user hashed to it loses their feed. Replicate the feed cache.
  • Fan-out cost is set by the poster with the most followers. The hybrid removes the celebrity case from the push path. Without it, one celebrity post would occupy the newsfeed generation service for everyone.
  • Pre-generated feeds lag reality. A new post reaches a follower's feed only after fan-out or the next refresh. That lag is what the 5-second target measures.
  • 150 TB of memory is the largest cost in the design. Storing 200 items instead of 500, and evicting inactive users, are the two ways to shrink it.

Where AI Fits in This Design

After the main design, interviewers often ask one more question: where would AI fit in this system?

This lesson already ranks the feed with a score built from signals: likes, comments, shares, recency, and whether a post has media. A learned model is the natural next step, predicting which posts a reader will interact with. The interview point is not the model. It is what the model needs from the system.

A model needs its signals at feed-build time, and it needs them fast. Counting likes across the posts database on every build is exactly the work the pre-generation design exists to avoid. The signals get their own store: a table or cache, keyed by post, updated as events arrive, read in bulk when a feed is built. Engineers call this a feature store. It is a genuinely new component, with its own freshness and failure questions, and naming it is what separates a real answer from "add ML".

Everything else stays. Fan-out still decides which posts reach a feed; the model only orders them once they are there. The celebrity rule, the per-user feed cache, and pagination work unchanged.

The lesson already names the second use: learning each user's login pattern, so feeds are pre-generated just before they are needed. The shape is the same: the model schedules the work, and the pipeline does it.

💡 In the interview: spend your time on feed generation and feed publishing, and move quickly through everything else. Say early that building the feed at request time cannot meet the 2-second target, and move to pre-generation. The moment the interviewer is listening for is when you name the celebrity problem and switch to the hybrid. Push for ordinary posters, pull for accounts with millions of followers. Keep the poster and the reader apart when you explain it. Fan-out cost follows the poster's follower count, not the reader's follow list. If you are asked what breaks first, name the cache server that holds a user's only copy of their feed.

Key takeaway: a newsfeed is a read-heavy system where every read merges posts from hundreds of accounts. About 17,500 feed requests per second, each over about 500 followed accounts, cannot be built at request time. Pre-generate each feed offline and keep about 200 to 500 items per user in a LinkedHashMap keyed by UserID. Page through it from the last FeedItemID the client saw. Deliver new posts by fan-out-on-write for ordinary posters and by pull for celebrities, because fan-out cost grows with the poster's follower count. Rank with signals like likes, comments, and recency. Shard the feed cache by UserID with consistent hashing, so a feed read touches one server. Keep media in blob storage, so a cached post stays about 1 KB.

P

poojanihalani19

· 3 years ago

Affinity is your relationship with users. It means that the amount a user has interacted with your Page in the past affects how likely they are to see your posts. So if they’ve clicked your links, liked or commented on your posts in the past, EdgeRank assumes they’re pretty keen to see more of your stuff, and gives it higher priority in that user’s News Feed.

Recently Facebook released an update to the News Feed algorithm called Last Actor. This update takes note of the last 50 interactions you’ve had on Facebook and gives content from those same users or Pages more prevalence in your feed. So if a user interacts with your Page in the morning, your content that afternoon or the next day will be more likely to show up in their feed (assuming they haven’t had another 50 interactions a

Show 1 reply
V

Vladimir

· 4 years ago

In section 4 "System APIs", can we use "used_id" instead of "api_dev_key"? What disadvantages of using "user_id" for throttling?

Show 2 replies
P

Prabhakar

· 4 years ago

In section 5, are the tables an RDBMS schema representation? If so, how would this scale given the number of users/entities and the UserFollow table may explode in size with 1B (users) x 1B (entities)

  • Assuming we have 1B users that can also be entities

Similarly FeedMedia table size can also be too large for an RDBMS DB.

  1. How would these be modeled in NoSQL and what DBs would be used?
  2. Can we use Cassandra for tracking both UserFollow (User as Key and all followed as column values), UserFeed (User as Key and Feeds as column values)? How would we store FeedMedia in this case?
Show 1 reply

Reading Progress

0%


Vote for new content

On This Page

What is Facebook's Newsfeed?

Try it yourself

Sketch it here

Step 1: Clarify the Requirements

Step 2: Estimate the Scale

Step 3: Define the API

Step 4: Define the Data Model

Step 5: Draw the High-Level Design

Step 6: Go Deep

Generating the feed

How much feed to keep in memory

Publishing the feed

Ranking the feed

Partitioning the data

Step 7: Bottlenecks and Failure Points

Where AI Fits in This Design