Skip to main content

355. Design Twitter

mediumAsked at Pinterest

Design Twitter is Pinterest's favorite social-graph design problem: build a feed where users follow others, post tweets, and read a merged most-recent-10 feed. The interviewer is checking whether you reach for the k-way-merge / min-heap pattern that production feed-serving uses.

By Alex Chen, Founder, InterviewChamp.AI · Last verified

Source citations

Public interview reports confirming this problem appears in Pinterest loops.

  • Glassdoor (2026-Q1)Pinterest backend / feed-infra onsite reports cite Design Twitter as the data-structure design round for feed-team SWEs.
  • LeetCode Pinterest tag (2026-Q1)High-frequency Pinterest-tagged problem; obvious analog to Pinterest's home-feed problem.
  • Reddit r/cscareerquestions (2025-11)Pinterest L4 candidate reports walking through this as a feed-design proxy.

Problem

Design a simplified social-graph service supporting four operations: postTweet(userId, tweetId), getNewsFeed(userId) (return the 10 most-recent tweet IDs from the user and anyone they follow, newest first), follow(followerId, followeeId), unfollow(followerId, followeeId).

Constraints

  • 1 <= userId, followerId, followeeId <= 500
  • 0 <= tweetId <= 10^4
  • All tweet IDs in a single test are unique.
  • At most 3 * 10^4 calls in total to postTweet, getNewsFeed, follow, and unfollow.

Examples

Example 1

Input
Twitter twitter; twitter.postTweet(1, 5); twitter.getNewsFeed(1); twitter.follow(1, 2); twitter.postTweet(2, 6); twitter.getNewsFeed(1); twitter.unfollow(1, 2); twitter.getNewsFeed(1);
Output
[null,[5],null,null,[6,5],null,[5]]

Explanation: After following user 2 and user 2 posting 6, user 1's feed shows 6 then 5. After unfollowing, only user 1's own tweets remain.

Approaches

1. Concatenate-and-sort (brute force)

On getNewsFeed, walk every followee plus self, collect every tweet, sort by timestamp descending, slice the first 10.

Time
O(T log T) per feed where T = total tweets across followees
Space
O(T)
class TwitterBrute {
  constructor() {
    this.tweets = new Map();
    this.follows = new Map();
    this.time = 0;
  }
  postTweet(userId, tweetId) {
    if (!this.tweets.has(userId)) this.tweets.set(userId, []);
    this.tweets.get(userId).push({ id: tweetId, time: this.time++ });
  }
  getNewsFeed(userId) {
    const followees = this.follows.get(userId) || new Set();
    const all = [];
    const include = new Set([userId, ...followees]);
    for (const u of include) {
      for (const t of this.tweets.get(u) || []) all.push(t);
    }
    all.sort((a, b) => b.time - a.time);
    return all.slice(0, 10).map((t) => t.id);
  }
  follow(followerId, followeeId) {
    if (!this.follows.has(followerId)) this.follows.set(followerId, new Set());
    this.follows.get(followerId).add(followeeId);
  }
  unfollow(followerId, followeeId) {
    this.follows.get(followerId) && this.follows.get(followerId).delete(followeeId);
  }
}

Tradeoff: Correct and simple. Falls over on real Pinterest scale (a user might follow thousands of boards) because we sort everything when we only need top-10.

2. Per-user tweet list + k-way merge with max-heap (optimal)

Keep each user's tweets in chronological order. For getNewsFeed, k-way merge across all followees using a max-heap by timestamp; pop 10.

Time
O(K log K + 10 log K) per feed where K = number of followees
Space
O(K) for the heap
class MaxHeap {
  constructor() { this.a = []; }
  push(item) { this.a.push(item); this._up(this.a.length - 1); }
  pop() {
    if (this.a.length === 0) return null;
    const top = this.a[0];
    const last = this.a.pop();
    if (this.a.length > 0) { this.a[0] = last; this._down(0); }
    return top;
  }
  size() { return this.a.length; }
  _up(i) {
    while (i > 0) {
      const p = (i - 1) >> 1;
      if (this.a[p].time < this.a[i].time) {
        [this.a[p], this.a[i]] = [this.a[i], this.a[p]];
        i = p;
      } else break;
    }
  }
  _down(i) {
    const n = this.a.length;
    while (true) {
      const l = 2 * i + 1, r = 2 * i + 2;
      let best = i;
      if (l < n && this.a[l].time > this.a[best].time) best = l;
      if (r < n && this.a[r].time > this.a[best].time) best = r;
      if (best === i) break;
      [this.a[best], this.a[i]] = [this.a[i], this.a[best]];
      i = best;
    }
  }
}

class Twitter {
  constructor() {
    this.tweets = new Map();
    this.follows = new Map();
    this.time = 0;
  }
  postTweet(userId, tweetId) {
    if (!this.tweets.has(userId)) this.tweets.set(userId, []);
    this.tweets.get(userId).push({ id: tweetId, time: this.time++ });
  }
  getNewsFeed(userId) {
    const include = new Set([userId, ...(this.follows.get(userId) || [])]);
    const heap = new MaxHeap();
    for (const u of include) {
      const list = this.tweets.get(u);
      if (!list || list.length === 0) continue;
      const idx = list.length - 1;
      heap.push({ ...list[idx], user: u, idx });
    }
    const out = [];
    while (heap.size() && out.length < 10) {
      const top = heap.pop();
      out.push(top.id);
      if (top.idx > 0) {
        const next = this.tweets.get(top.user)[top.idx - 1];
        heap.push({ ...next, user: top.user, idx: top.idx - 1 });
      }
    }
    return out;
  }
  follow(followerId, followeeId) {
    if (!this.follows.has(followerId)) this.follows.set(followerId, new Set());
    this.follows.get(followerId).add(followeeId);
  }
  unfollow(followerId, followeeId) {
    this.follows.get(followerId) && this.follows.get(followerId).delete(followeeId);
  }
}

Tradeoff: Production-shaped: each followee contributes only one pointer at a time, and we pop exactly 10. This mirrors the fan-out-on-read pattern Pinterest's home-feed service uses for users with bounded followee counts.

Pinterest-specific tips

Pinterest interviewers want you to volunteer the production analog: 'this is fan-out-on-read for a home-feed; for celebrities with 10M followers you'd switch to fan-out-on-write.' Don't go deep on that distinction unprompted, but signal that you know it exists. Use a single monotonically-increasing timestamp counter — wall-clock time creates ties.

Common mistakes

  • Walking every tweet across every followee instead of using the heap — fails at scale.
  • Using wall-clock Date.now() — two posts in the same millisecond collide.
  • Forgetting to include the user's OWN tweets in their feed.
  • Pushing all tweets into the heap up front instead of streaming pointers — defeats the whole optimization.

Follow-up questions

An interviewer at Pinterest may pivot to one of these next:

  • Now handle 10M followers per celebrity: switch to fan-out-on-write for hot users.
  • Add 'mute' (block specific users from showing up in feed).
  • Persist tweets to a database — what does the schema look like?
  • Support 'retweets' — does that change your data model?

Solve it now

Free. No sign-up. Python and JavaScript run instantly in your browser.

Output

Press Run or Cmd+Enter to execute

FAQ

Why does Pinterest care about Design Twitter specifically?

Pinterest's home-feed is structurally identical: users follow boards / topics / other users; the home feed merges the most-recent pins across all sources. The k-way-merge pattern is the same primitive.

Is the heap version really better than sort-everything for n = 500?

Asymptotically yes; at the LeetCode constraint sizes the difference is negligible. The interview signal is whether you reach for the right pattern — not raw runtime.

Free learning resources

Curated free links for this problem.

Practice these live with InterviewChamp.AI

Drill Design Twitter and other Pinterest interview questions under real-loop conditions with instant feedback on your reasoning, complexity claims, and code.

Practice these live with InterviewChamp.AI →