Fan-out is what happens when one event has to reach many recipients: a post reaches every follower's feed, a message reaches every group member, an order event reaches email, analytics and inventory. Where you do that work, when it's written (push) or when it's read (pull), is one of the most important decisions in feed, notification and messaging designs.
A teacher can photocopy the notice and put one in every student's bag (push: work up front, easy to read later), or pin one copy on the notice board and let students come and read it (pull: little work up front, everyone walks to the board).
PUSH: when Alice posts, write the post id into each follower's feed
post --> [fan-out workers] --> feed:bob [p9, p7, ...]
\--> feed:carol [p9, p3, ...]
read feed = read one precomputed list (fast reads)
cost: one write per follower (celebrity = millions of writes)
PULL: when Bob opens his feed, fetch recent posts of everyone he follows
read feed = for each followee: get recent posts -> merge -> sort -> top N
cost: one read per followee, at read time (slow for users following many)
writes are cheap (store the post once)
Most followers-of-normal-users relationships use push: a typical account has hundreds of followers, so the write cost is small and reads stay fast. Accounts with huge follower counts (celebrities) are excluded from push; their posts are pulled at read time and merged into the precomputed feed.
feed(bob) = precomputed_feed(bob) # pushed from normal users
+ recent_posts(c) for c in celebrities_bob_follows # pulled, cached
-> merge by time/rank -> top 50
Other refinements: skip pushing to inactive users (compute their feed on their next login), cap feed length (keep the latest ~500–800 ids), and store only post ids in feeds, hydrating post content from a cache.
Fan-out to systems (not users) usually goes through publish/subscribe: a producer publishes OrderPlaced once to a topic, and each subscriber (email, analytics, search index, inventory) has its own subscription and processes independently at its own pace. Adding a new consumer requires no change to the producer.
Push buys fast, predictable reads with write amplification and storage for every feed. Pull buys cheap writes and fresh data with expensive, latency-sensitive reads. The hybrid gets most of both at the cost of two code paths and a threshold to tune.