Push vs Pull Architecture and APIs: System Design Interview Guide
Quick Overview
Explains push vs pull at the architecture level (fan-out on write vs fan-out on read) and at the API level (webhooks, SSE, and streaming vs polling). Includes a trade-off table you can rebuild on a whiteboard, a worked hybrid feed example, and real interview questions from Amazon, Confluent, and DoorDash.
Push vs Pull Architecture and APIs: 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 section on push vs pull at the API boundary specifically, 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.
Key Takeaways
- Push vs pull is about who initiates the transfer, not which way the bytes flow. Say that in your first sentence and derive every trade-off from it.
- Decide with one ratio: read rate vs write rate, weighted by fan-out. Reads dominate: push (fan-out on write). Huge fan-out or dormant readers: pull (fan-out on read). Real systems go hybrid.
- At the API boundary, a push API (webhooks, SSE, streaming RPC) earns its complexity only when changes are sparse, consumers must react within seconds, and consumers are reachable. Otherwise expose a pull API and make polling cheap with conditional requests.
- Any push proposal needs a backpressure story: retries, dedupe, dead-letter handling. Propose push without one and the follow-up question writes itself.
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
Asked at DoorDash — Design an alert notification system Build a multi-tenant alerting platform: monitoring sources emit events, users configure alert rules, routing policies, and escalation chains, and responders get notified over email, SMS, push, chat, and phone. The stated targets - millions of alert events a day, p95 time-to-first-notification under 30 seconds, at-least-once delivery - force a push architecture, so the interview turns on your retry, dedupe, and backpressure story rather than the push/pull choice itself.
Operational alerting and social notifications share a skeleton: one event has to reach N interested parties inside a latency budget. The social version is where N turns pathological. 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
Asked at Amazon — Design a Scalable News Feed Design the home feed for a social product: users publish posts, follow accounts, and request a paginated feed of posts they're allowed to see. The prompt deliberately leaves scale, ordering, privacy, deletion, and freshness unstated - and the answers you extract to those questions are what decide fan-out on write versus fan-out on read.
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. For the cost-accounting version of this argument - who pays for delivery, the writer or the reader - see Designing a News Feed: Who Pays for the Delivery?
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.
Push vs pull APIs
The same question shows up at the API boundary, and it's worth answering in API vocabulary because interviewers ask it that way. A pull API is the default request/response model: the consumer calls GET /orders?since=... whenever it wants data, and the provider answers. A push API inverts the initiation: the provider delivers data to the consumer - a webhook POST to a URL the consumer registered, a server-sent event stream, or a streaming RPC - without waiting to be asked. Who calls whom is the entire distinction, and it's covered in depth in Webhook vs API: Who Calls Whom, and What Interviewers Actually Probe.
Asked at Confluent — Design an RSS News Feed Service Each user picks a set of RSS feeds to follow. A scheduler fetches fresh items from those publishers, records the metadata, and builds a per-user timeline out of whatever it collected. The interviewer weights two things above all: table design and API design.
That prompt is the pull side taken seriously. RSS is a polling protocol - nobody pushes to you - so the real design work is the poll scheduler, dedupe on stable item IDs, and making the no-change case nearly free with conditional fetches. Assume publishers push and the interview goes sideways in the first ten minutes.
When should an API push? When three conditions hold at once: changes are sparse relative to how often consumers would poll, consumers genuinely need to react within seconds, and consumers can be reached - they run a server that can accept a webhook, or they hold a connection open for SSE. Payment status updates and CI build results fit. If any condition fails - browser and mobile clients with no inbound endpoint, thousands of third-party consumers you'd have to track and retry against, or data that changes so often you'd be pushing continuously - expose a pull API and let consumers poll with ETag/If-Modified-Since so unchanged responses are cheap.
One nuance worth stating precisely: what a push API decouples is the triggering event from the consumer's processing of it. The delivery itself is usually a synchronous HTTP POST the provider waits on, times out, and retries - which is exactly why push providers need the retry and dead-letter machinery covered later in this guide. If you're fuzzy on where that sync/async boundary sits, Synchronous vs Asynchronous: Where the Boundary Goes in a System Design Interview walks the same reasoning.
Message brokers make the choice explicit inside the backend, too. Kafka consumers pull from partitions - that's a deliberate design choice that gives consumers natural backpressure and replay, at the cost of managing offsets. Classic RabbitMQ pushes messages to consumers, which is lower-latency for sparse traffic but needs prefetch limits to keep a fast producer from drowning a slow consumer. The comparison is worked through in Kafka vs. RabbitMQ: Architectural Differences for System Design Interviews.
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 questions specifically, 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 is the difference between a push API and a pull API?
Who initiates each transfer. With a pull API the consumer calls the provider (a normal REST request, or a poll loop) whenever it wants data. With a push API the provider initiates delivery - a webhook POST, a server-sent event, or a streaming RPC - the moment something changes, without being asked.
When should an API push instead of being polled?
When changes are sparse, consumers need to react within seconds, and consumers are reachable - they can accept a webhook or hold a connection open. If consumers are browsers or mobile apps with no inbound endpoint, or there are thousands of third parties you'd have to track and retry against, a pull API with conditional requests is the safer default.
Is Kafka push or pull?
Kafka consumers pull from partitions. That's deliberate: pulling gives each consumer natural backpressure and lets it replay from an offset. RabbitMQ, in its classic mode, pushes to consumers instead, which is why prefetch limits exist to protect slow consumers.
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.
Comments (0)