Push vs Pull Architecture: A System Design Interview Guide
Quick Overview
This guide explains push versus pull architecture for system design interviews, covering decision criteria and trade-offs such as latency, freshness, write amplification, and operational complexity across examples like notification fan-out, news feeds, cache invalidation, CDN distribution, metrics collection, and polling/webhooks/SSE.
Push vs Pull Architecture: A System Design Interview Guide
"Would you push or pull here?" is one of the most common forks in a system design interview, and it shows up in more places than candidates expect: notification fan-out, news feeds, cache invalidation, CDN content distribution, metrics collection, and the choice between polling, webhooks, and server-sent events. The interviewer is rarely asking for trivia. They want to see whether you can reason about who initiates data movement and what that costs in latency, freshness, write amplification, and operational complexity.
This guide gives you the mental model, a trade-off table you can reconstruct on a whiteboard, three concrete scenarios where the answer differs, a fully worked code example, and a scripted answer to the classic follow-up: "When would you choose pull over push?"
Video companion: This verified YouTube video gives a second pass on the same prep area.
The core distinction
In a push system, the producer (or an intermediary acting on the producer's behalf) sends data to consumers as soon as it's available. The consumer is passive; it receives. A webhook is push: GitHub POSTs to your endpoint when a commit lands.
In a pull system, the consumer requests data when it wants it. The producer is passive; it responds. Polling an API every 30 seconds is pull. So is a read-through cache that fetches from the database on a miss.
The thing that trips people up: push and pull are about who initiates the transfer, not about which direction the bytes flow. A push notification and a polled API response both move bytes from server to client. What differs is the trigger. That distinction is the whole interview.
A second axis matters just as much: when does the work happen - at write time or at read time? This is "fan-out on write" (push) versus "fan-out on read" (pull), and it's where most feed-design interviews live. Pushing moves the cost to write time and precomputes results; pulling defers cost to read time and computes on demand. Everything below is a consequence of those two choices.
The trade-off table
| Dimension | Push (fan-out on write / server-initiated) | Pull (fan-out on read / client-initiated) |
|---|---|---|
| Latency to consumer | Low - data arrives as soon as it's produced | Higher - bounded by poll interval or request timing |
| Freshness/staleness | Always fresh; consumer sees the latest | Can be stale up to one poll interval |
| Write amplification | High - one event may write to N consumers | Low - write touches one place |
| Read cost | Cheap - result is precomputed | Expensive - computed/assembled per request |
| Wasted work | Writes to inactive/absent consumers are wasted | Polls that return "no change" waste round-trips |
| Producer complexity | Higher - must track subscribers, retry, dedupe | Lower - stateless responder |
| Consumer complexity | Lower - just handle inbound events | Higher - must schedule polls, handle backoff |
| Scales poorly when | Fan-out factor is huge (celebrity problem) | Read rate ≫ change rate (polling overhead) |
| Backpressure | Hard - producer can overwhelm slow consumers | Natural - consumer pulls at its own pace |
| Coupling | Producer must know/reach consumers | Loosely coupled via a known endpoint |
You don't need to memorize this. You need to be able to derive a row by asking "what happens to this cost as the system grows?" If the interviewer pushes on scalability, walk the column that breaks first.
Three scenarios where the answer flips
1. Notification fan-out
A user posts; their followers should be notified. Push (fan-out on write) precomputes each follower's notification feed at post time. Reads are then a single lookup - great when users check notifications far more often than producers post.
It breaks on the celebrity problem. A user with 50 million followers triggers 50 million writes per post. Now write latency spikes, the queue backs up, and a burst of celebrity activity can saturate your fan-out workers.
The standard answer is a hybrid: push for normal accounts, pull for celebrities. Followers of a celebrity pull that user's recent posts at read time and merge them with their pushed feed. This is exactly how large feed systems (Twitter/X's historical design is the canonical example) avoid the write explosion while keeping the common case cheap.
2. News feed delivery
Same push/pull axis, different read pattern. If most users open the app many times a day, fan-out-on-write wins: you pay the write cost once and serve cheap reads forever. If most users are dormant - you'd push to millions of inboxes nobody opens - fan-out-on-read wins: you only assemble a feed for users who actually show up.
The deciding ratio is read rate vs. write rate, weighted by fan-out factor. High reads + low writes + moderate fan-out → push. Low reads + high writes + huge fan-out → pull. Realistic systems sit in the middle and go hybrid.
3. Polling vs. webhooks vs. SSE (client-server freshness)
When a client needs fresh data from a server, you have a few options, and naming all of them signals depth:
- Polling (pull): client asks every N seconds. Simple, works through any firewall, but trades freshness for wasted requests. Most polls return "nothing new."
- Long polling (pull-ish): client opens a request the server holds open until data is ready, then re-opens. Reduces empty responses; still one connection per client.
- Webhooks (push): server POSTs to a client-provided URL on change. Zero waste, but requires the client to run a reachable HTTP endpoint - bad for browsers and mobile.
- Server-Sent Events / WebSockets (push): server streams over a held-open connection. Great for browsers; costs a persistent connection per client, which constrains how many concurrent consumers one box can hold.
The rule of thumb: push when changes are infrequent but freshness matters and consumers are reachable; pull when consumers are numerous, transient, or behind firewalls, or when the change rate is high enough that you'd be pushing constantly anyway.
Honorable mention: CDN and cache invalidation
CDNs are mostly pull: the edge fetches from origin on a cache miss (read-through), then serves cached copies until TTL expiry. That's why a CDN change has propagation lag. Push invalidation (purge/prefetch) exists for when you can't tolerate that lag - you actively push new content or purge keys to the edge. Same trade-off: pull is simpler and lazy; push is fresher and costs you a fan-out to every edge node.
A worked example: hybrid feed fan-out
Let's make the celebrity-problem hybrid concrete. The decision rule: if an author has more than a threshold number of followers, don't push to all of them - those followers will pull the author's posts at read time instead. Everyone else gets a normal push into a precomputed feed.
from dataclasses import dataclass
import heapq
CELEBRITY_FOLLOWER_THRESHOLD = 10_000
@dataclass
class Post:
post_id: int
author_id: int
created_at: float
body: str
class FeedService:
# Hybrid push/pull feed.
# - Push: normal authors fan out on write into each follower's feed cache.
# - Pull: celebrity authors are NOT fanned out; followers pull their
# recent posts at read time and merge them in.
def __init__(self, social_graph, post_store):
self.graph = social_graph # follower / following lookups
self.posts = post_store # author_id -> recent posts
self.feed_cache = {} # user_id -> list[Post] (push targets)
def on_new_post(self, post: Post) -> None:
# Write path. Fan out on write only for non-celebrity authors.
self.posts.add(post)
follower_count = self.graph.follower_count(post.author_id)
if follower_count <= CELEBRITY_FOLLOWER_THRESHOLD:
# PUSH: precompute each follower's feed now.
for follower_id in self.graph.followers(post.author_id):
self.feed_cache.setdefault(follower_id, []).append(post)
# else: PULL - do nothing on write; resolved at read time.
def get_feed(self, user_id: int, limit: int = 50) -> list:
# Read path. Merge the pushed feed with pulled celebrity posts.
pushed = self.feed_cache.get(user_id, [])
# PULL the celebrities this user follows.
pulled = []
for author_id in self.graph.following(user_id):
if self.graph.follower_count(author_id) > CELEBRITY_FOLLOWER_THRESHOLD:
pulled.extend(self.posts.recent(author_id, limit))
# Merge by recency. heapq.nlargest is O(n log limit), which beats
# a full sort when the candidate set is much larger than `limit`.
merged = heapq.nlargest(
limit,
pushed + pulled,
key=lambda p: p.created_at,
)
return merged
A few things to call out, because an interviewer will:
- The write path is cheap for celebrities and the read path is cheap for everyone else. We never pay the 50-million-write cost. We pay a bounded read-time merge instead, and the number of celebrities any single user follows is small.
heapq.nlargest(limit, ...)is the right primitive for the merge. A fullsorted()isO(n log n);nlargestisO(n log limit). Whenn(total candidate posts) is large butlimitis 50, that's a real win, and saying so signals you think about read-path cost.- The threshold is the tuning knob. Set it too low and you push too little, making reads expensive for everyone. Set it too high and a borderline-celebrity post still triggers a write storm. In production you'd tune it from real follower-distribution data, and you'd say that out loud.
- What's missing on purpose: ranking, deduplication of a post that's both pushed and pulled, pagination cursors, and feed-cache eviction. Name them as next steps. Listing what you deliberately deferred is a senior signal; pretending the toy version is complete is a junior one.
How to answer this in an interview
When the interviewer asks "push or pull?", resist the urge to pick one. The strong answer is a three-step move:
- State the deciding variables. "This depends on the read-to-write ratio and the fan-out factor. Let me figure out which regime we're in." Now you've shown you know the answer is conditional.
- Estimate the numbers. "If the average user reads their feed 20 times a day and posts twice, reads dominate, so I'd lean push and precompute." Even rough Fermi estimates beat hand-waving.
- Identify the failure mode and patch it. "Pure push breaks on high-fan-out accounts, so I'd go hybrid: push for the body of the distribution, pull for the tail." This is the move that separates senior from mid-level answers.
Anti-patterns to avoid:
- Picking push or pull as a personality trait. "I always prefer push because it's faster" tells the interviewer you don't reason about cost.
- Forgetting the consumer's environment. Webhooks are useless to a mobile client that can't host an endpoint. Always ask: can the consumer be reached, and how many are there?
- Ignoring backpressure. Push systems need a story for slow or absent consumers - queues, dead-letter handling, retry-with-backoff, drop policies. If you propose push and don't mention what happens when a consumer falls behind, expect a follow-up.
Common follow-up questions
"When would you choose pull over push?" When the fan-out factor is huge relative to the read rate (you'd be precomputing for consumers who never read), when consumers are too numerous or transient to track as subscribers, when consumers live behind firewalls or NAT and can't receive an inbound connection, or when the consumer needs to control its own consumption rate for backpressure. The celebrity tail of a feed, browser clients without a public endpoint, and batch ETL jobs that read on their own schedule all favor pull.
"How do you handle the celebrity / hot-key problem in a push system?" Go hybrid: stop fanning out above a follower threshold and resolve those authors at read time. Optionally cache the celebrity's recent posts aggressively (they're read by everyone, so the cache hit rate is excellent) so the read-time pull is cheap.
"Push has lower latency - why ever poll?" Because push's low latency is paid for in write amplification, subscriber tracking, and backpressure complexity. When changes are rare and a few seconds of staleness is fine, polling is dramatically simpler and degrades gracefully. Simplicity is a feature when the latency requirement doesn't demand otherwise.
"How do you make polling less wasteful?"
Long polling (hold the request open until data is ready), conditional requests (ETag / If-Modified-Since so unchanged responses are a cheap 304), adaptive intervals (back off when idle, tighten when active), and batching multiple resources into one poll.
"Push at-least-once or at-most-once?" Almost always at-least-once with idempotent consumers. Exactly-once delivery is effectively impossible over an unreliable network; you get exactly-once processing by making the consumer dedupe on a message ID. If you claim exactly-once delivery in an interview, expect to defend it.
"How does this apply to cache invalidation?" Pull = lazy expiration (TTL; the cache refetches on the next miss). Push = active invalidation (the source purges or updates cache entries on change). Pull tolerates staleness up to the TTL and is simpler; push keeps caches fresh but requires the source to fan out invalidations to every cache node - the same write-amplification trade-off in a different costume.
How to Use This Page as a Prep Plan
Do not treat this as passive reading. Convert the ideas in this page into a short weekly loop: learn one idea, practice it under interview conditions, then write down what changed. That is the fastest way to turn advice into visible interview behavior.
| Prep area | What you need to prove | Practice artifact |
|---|---|---|
| Requirements | Clarify the problem before drawing boxes. | Functional and non-functional checklist. |
| Architecture | Map data flow before naming technologies. | One end-to-end diagram. |
| Tradeoffs | Explain why the design fits the constraints. | Latency, consistency, cost, and operability notes. |
| Failure handling | Show how the system behaves under stress. | Backpressure, retries, monitoring, and rollback plan. |
For Push vs Pull Architecture: A System Design Interview Guide, the strongest candidates usually do three things well: they make their assumptions explicit, they use concrete examples instead of vague claims, and they review mistakes quickly enough that the next practice rep is better than the last one.
Conclusion
Push and pull aren't competing philosophies; they're two ends of a dial you set per workload. Push trades write cost and producer complexity for low latency and cheap reads. Pull trades freshness and read cost for simplicity and natural backpressure. The right answer in an interview is almost always "it depends on the read/write ratio and fan-out - here's how I'd measure it, and here's the hybrid I'd reach for when the pure version breaks."
If you want to drill this until the reasoning is automatic, PracHub has system design and notification-fan-out problems where you can practice the full push-vs-pull derivation against a timer. The pattern recurs constantly - get it cold once and it pays off across feeds, caches, CDNs, and every "should this be a webhook or a poll?" decision you'll ever make.
FAQ
What should I draw first in a system design interview?
Start with users, requests, core data, and the main read or write path. Add scale mechanisms only after the simple design is clear.
How detailed should the design be?
Deep enough to defend the main bottleneck. A focused design with good tradeoffs beats a crowded diagram with no reasoning.
How do I avoid sounding memorized?
Tie each component to a requirement and explain the tradeoff it creates.
Related Articles
From Non-CS Major to Software Engineer: A Practical Guide to Cracking the Technical Interview
Prepare for technical interviews with a practical guide to DSA practice, live coding, mock interviews, communication, and interview mindset.
From Non-CS Major to Software Engineer: A Practical Guide to Cracking the Technical Interview
Prepare for technical interviews with a practical guide to DSA practice, live coding, mock interviews, communication, and interview mindset.
Design WhatsApp: the presence and receipt problems most candidates ignore
Design WhatsApp-style chat with WebSockets, offline inboxes, Kafka partitions, presence TTLs, receipts, and reliable delivery.
I Pinned Our Autoscaler for a Month to See What Would Break. Nothing Did.
Learn when Kubernetes autoscaling helps, when CPU-based HPA wastes money, and how capacity planning can cut cloud costs safely.
Comments (0)