CASE STUDY

Post Privacy and Visibility System (Facebook-style)

3 min read·485 words·Advanced

Asked at

1 candidate report in Feb 2026

How to use this case study

SDE-2 / Mid

Define the privacy options (Public, Friends, Friends-of-friends, Only me, Custom) and a check(viewer, post) function.

SDE-3 / Senior

Enforce privacy everywhere posts appear (feed, profile, search, notifications, shares), with fast friend-graph lookups and caching.

Staff / Principal

Handle changes (unfriending, privacy edits) consistently, filtering at scale in feeds and search, and auditing for privacy bugs.


0) Problem Restatement

Meta asked: design the privacy system for social media posts. Each post has an audience:

  • Public: anyone.
  • Friends: only the author's friends.
  • Friends of friends (optional).
  • Only me.
  • Custom: specific people or lists allowed, with some excluded ("Friends except Alex").

Privacy must be enforced everywhere posts show up: news feed, profile pages, search, notifications, shares and comments, and must react correctly when someone unfriends or the author changes the setting.


1) The Core Check

can_view(viewer, post):
  if viewer == post.author: return True
  if viewer blocked by / blocking author: return False
  switch post.audience:
    PUBLIC:            return True
    ONLY_ME:           return False
    FRIENDS:           return are_friends(viewer, author)
    FRIENDS_OF_FRIENDS:return are_friends(viewer, author) or share_friend(viewer, author)
    CUSTOM:            return (viewer in allow_list or viewer in allowed_lists) and viewer not in deny_list

Rules to state: deny beats allow, blocking beats everything, and the author can always see their own post.


2) Data and Architecture

Architecture Diagram

flowchart LR
    SVC["Feed / profile / search / notifications"] --> PC["Privacy check service - batch API"]
    PC --> FG[("Friend graph - cached adjacency")]
    PC --> PA[("Post audience - stored with post")]
    PC --> BL[("Block lists")]
    PC --> CL[("Custom lists")]
    ED["Privacy edits / unfriend events"] --> INV["Cache invalidation"]
    INV --> PC
  • Post audience is stored with the post: audience_type, allow_ids, deny_ids, allowed_list_ids.
  • Friend graph: a sharded graph store with an in-memory cache of each user's friend set. are_friends = a set lookup. "Friends of friends" = check whether the two friend sets intersect (bounded work, with caching).
  • Privacy check service exposes a batch API (can_view(viewer, [post_ids])), because feeds check hundreds of posts at once.


3) Enforcing It Everywhere

  • Feed: candidate posts are generated broadly, then filtered with the batch check before ranking results are returned. Never cache a rendered feed across viewers.
  • Profile: the same check for each post on the page.
  • Search: the index stores audience data. Public posts go in a public index. For friends-only posts, filter at query time using the viewer's friend set (e.g., author ∈ viewer's friends), then run the exact check on the final page of results.
  • Notifications and shares: a share can never widen the original audience. A reshared "Friends" post is visible only to people who can see the original. Comments inherit the post's audience.
  • Defense in depth: the final response layer re-checks items, so a bug in one service doesn't leak data.


4) Changes and Consistency

  • Author changes the audience: update the post's audience, and invalidate caches and precomputed feeds that included it. Future requests use the new setting immediately, since checks happen at read time.
  • Unfriend: update the friend graph, and invalidate both users' friend-set caches. Friends-only posts stop showing right away, because checks read the current graph.
  • Why check at read time: precomputing "who can see what" is huge and goes stale. A fast check with cached friend sets is simpler and always current.


5) Wrap-Up

Store each post's audience (type + allow/deny lists) with the post, and evaluate a clear can_view rule set (author sees own, blocks and denies win, then public, friends, friends-of-friends or custom lists) against a cached, sharded friend graph through a batch privacy service. Enforce it at read time in every surface (feed, profile, search, notifications, shares), never let shares widen the audience, re-check at the response layer, and invalidate caches on audience changes and unfriending.

More Case Studies

Practice with a Mock Interview

Apply what you learned in a live system design mock interview with our AI interviewer.

Start System Design Interview →