InterviewKata
System design · Worked design D08 of 46 · Social and content · Long form

Design a news aggregator

Collect articles from thousands of publishers and show each reader a fast, endless, up-to-date feed for their region. It has two halves with opposite characters: a background ingestion pipeline that's write-light and can take minutes, and a feed that's read-heavy, must load in under 200 ms, and must survive the whole world refreshing at once when big news breaks. It follows the interview framework phase by phase.

1. The prompt

"Design Google News: aggregate articles from publishers worldwide into a feed per region; clicking an article opens it on the publisher's site."

Ingestion is small: about 12 articles a second. The feed is the challenge: thousands of reads a second normally, a million a second during breaking news, with fresh results. The design separates the two halves and makes the read side almost entirely cached.

2. Frame the problem

Where do articles come from?

RSS/Atom feeds from most publishers, webhooks from a few partners, some sites with no feed at all.

Ingestion needs polling tiers, push support, and maybe a small crawler.

Personalised feeds, or one per region?

One feed per region to start; light personalisation later.

A per-region feed is tiny and shared by everyone, so it can be precomputed and cached.

How fresh?

New articles within ~30 minutes; within a few minutes for major publishers.

Polling intervals per publisher tier.

How big are the spikes?

From 100M daily readers to 500M during breaking news, many refreshing every few seconds.

The read path must never touch the database; CDN and cache replicas take the load.

Do we host the articles?

No: titles, summaries and thumbnails; the click goes to the publisher.

We store small records and thumbnails, not article bodies.

Is a slightly stale feed acceptable?

Yes, a few seconds or minutes. An error page isn't.

Availability over consistency on the read path.

3. What it must do

  1. The system ingests new articles from publishers continuously.
  2. A reader sees a feed of recent articles for their region (title, summary, thumbnail, publisher), with infinite scroll.
  3. Clicking an article opens the publisher's page (and we count the click).

Not now:

4. How well it must do it

TargetWhy this number
Fast: the feed loads in under 200 ms speedIt's the front page.
Spike-proof: 1M+ feed requests a second during breaking news load shapeBig news is exactly when people come.
Fresh: articles appear within ~30 minutes, within ~5 for major publishers speedAn aggregator that's an hour behind is useless.
Available over consistent: a slightly stale feed beats an error consistency or availabilityNobody notices seconds of staleness; everyone notices errors.
Polite: polling uses conditional requests and respects publishers' limits rulesPublishers are partners; hammering them gets you blocked.

5. Numbers that change the design

QuantityWorking→ so we…
Feed reads100M readers × 5 loads a day ÷ 10⁵~5,000/s normally; 1M+/s in spikes → so cache and CDN, never the database
Articles50,000 publishers × ~20 articles a day1M a day ≈ 12/s → so ingestion load is tiny
Feed sizeEach region's latest ~2,000 articles × ~1 KB~2 MB per region → so every region's feed fits in memory many times over
Polling50,000 publishers every 60 min~14 polls a second; cheap with conditional requests

6. The nouns

publishers  publisher_id, name, feed_url, region, tier (fast | normal | slow), etag, last_modified, next_poll_at
articles    article_id (time-ordered), publisher_id, url (unique), title, summary, thumbnail_key, region, category, published_at
feed:{region}  sorted set of article_id (latest 2,000), with category in each entry

7. The contract

GET /v1/feed?region=IN&category=tech&cursor=<article_id>&limit=20 → {articles: [...], next_cursor}
GET /a/{article_id}                → 302 to the publisher's URL (logs the click)
POST /v1/publishers/{id}/webhook   (partners push new articles; signed)

Data flow: (1) poll or receive from a publisher → (2) parse, dedupe by URL, store the article → (3) download and resize its thumbnail → (4) change data capture adds it to the regional feed → (5) readers get the feed from the cache or CDN.

8. First working version

Ingestion workers, an articles table, and a feed service that queries it.

8.1 Ingesting articles

A scheduler hands publishers to workers when they're due. A worker fetches the feed, parses new items, skips URLs it has already stored, and inserts the rest.

8.2 Reading the feed

The feed service runs SELECT … WHERE region = ? ORDER BY published_at DESC LIMIT 20 OFFSET ? and returns titles with publisher-hosted image URLs.

8.3 Clicking through

A redirect endpoint logs the click and sends a 302 to the publisher.

Parked for later (section 9 fixes each)

  1. Offset pagination shows duplicates and gaps as news keeps arriving.
  2. A database query per feed load collapses during a spike.
  3. Polling everyone equally is either too slow for big publishers or too wasteful for small ones.
  4. Hotlinked publisher images are slow and break.
  5. Breaking-news traffic in one region.
  6. Categories and personalisation.

9. Make it hold up

9.1 Infinite scroll while news keeps arriving fast

ApproachWhy
✗ BadPage numbers or offsetsNew articles shift every page: readers see duplicates and miss stories (Module 07).
~ GoodA cursor on published_atStable, but articles sharing a timestamp are skipped or repeated at page edges.
✓ GreatA cursor on a time-sortable unique article id (Snowflake-style, Module 20)Unique and ordered: WHERE id < cursor ORDER BY id DESC LIMIT 20, or a range read on the sorted set.

9.2 A feed in under 200 ms at a million requests a second fast · spike-proof

ApproachWhy
✗ BadQuery the articles table on every loadFine at 5,000/s with an index; fails during a spike.
~ GoodCache each region's feed with a 30-minute TTLFast, but stale for up to 30 minutes, and a stampede when the hot key expires.
✓ GreatPrecomputed regional feeds: when an article row is inserted, change data capture (Module 13) emits an event; feed workers add it to the region's sorted set (ZADD feed:IN) and trim to the latest 2,000. Reads are a range on the sorted set; the first page is also cached at the CDN for a few seconds.Fresh within seconds, no expiry stampedes, and reads never touch the database.

9.3 Getting articles in quickly fresh · polite

9.4 Thumbnails fast

Don't hotlink publisher images: they're slow, vanish and change. Download each image once, store resized versions in object storage, and serve them through a CDN in several sizes (srcset). The CDN absorbs nearly all image traffic.

9.5 Breaking news spike-proof · available

When a huge story breaks, traffic multiplies within minutes. The feed service is stateless and autoscales; regions are separate stacks, so a spike in one doesn't hurt others. The regional sorted set is tiny, so throughput (not memory) is the limit: add read replicas of the cache, and let the CDN serve the first page for a few seconds at a time. Being a few seconds stale is invisible; being down is not.

9.6 Categories and light personalisation fast

9.7 The whole design after the deep dives

An article from a partner's webhook reaching a reader's feed, through every part.

10. Interviewer follow-ups

The same story appears from 40 publishers.

Cluster similar articles: compute text embeddings or shingles for each title and summary, group near-duplicates published close in time, and show one story card with "and 39 more sources". Run clustering as a stream job; feed entries reference the cluster.

A publisher's feed goes from 20 to 2,000 items a day overnight.

Probably spam or a broken feed. Cap items per publisher per hour into the feed, flag anomalies, and quarantine until reviewed.

Traffic grows 10×.

More regional stacks and cache replicas; the CDN does most of the work. Ingestion barely changes. Story clustering becomes worth doing because feeds get noisier.

A publisher asks us to remove an article.

Mark it removed in the articles table; change data capture removes it from regional feeds and the CDN is purged for the affected pages. The thumbnail is deleted from storage.

Show "trending now".

Count clicks per article per region in time buckets (Design D17/D20 style) and keep a top-K list updated every minute.

How do you know ingestion is healthy?

Track per-publisher freshness (time since the last new article, compared with its normal rate), poll errors, and the end-to-end delay from publish time to feed entry for top publishers.

Paywalled publishers?

Show what they allow in their feed (title and snippet), mark the card, and respect their terms. The click still goes to their site.

11. When things break

FailureWhat users seeHow we detect itWhat limits the damage
Feed cache node failsNothingReplica healthReplicas serve; CDN covers the first page
CDC pipeline stallsFeed stops updatingFreshness alert (newest article age)Feed keeps serving stale; catch up when CDC resumes
A publisher's feed breaksIts articles stop appearingPer-publisher freshnessBackoff, alert, keep others unaffected
Breaking-news spikeNothing, ideallyRequest rate per regionCDN, cache replicas, autoscaling, regional isolation
Image storage slowMissing thumbnailsImage errorsCDN cache; placeholder images

12. Wrap-up and the clock

Trade-offs we made:

What we'd watch:

At 10×:

A 45-minute interview

0–4Frame the problem (sources, regional vs personal, spikes)
4–9Must do, how well, numbers
9–13Nouns, contract, data flow
13–20First working version, parked list
20–40Deep dives: pagination (9.1), precomputed feeds (9.2), ingestion (9.3), spikes (9.5)
40–45Follow-ups and wrap-up

A 60-minute interview

0–5Frame the problem
5–11Must do, how well, numbers
11–16Nouns, contract, data flow
16–24First working version
24–50Deep dives 9.1–9.6 and the final picture
50–60Follow-ups: clustering, trending, takedowns; wrap-up
LevelWhat a strong answer shows
Mid-levelIngestion workers, an articles table, a feed API with pagination, thumbnails in object storage behind a CDN.
SeniorCursor pagination done right, precomputed regional feeds via CDC, polling tiers and webhooks, spikes handled with caches, replicas and regional stacks.
Staff+Personalisation that scales, story clustering, publisher relationships and legal constraints, cost and freshness trade-offs stated with numbers.

Common mistakes:

13. Try it, check yourself, read more

How quickly do new articles appear, and how many requests does ingestion make? Mix polling tiers and webhooks.

50,000
2%
5 min
60 min
10%

Model: an article is published at a random moment between polls, so on average it waits half the interval; webhook publishers notify within seconds. Articles are weighted toward top publishers (they publish ~20× more). Webhook publishers aren't polled.

What's the best cursor for an infinite news feed?

Unique and ordered, so no duplicates or gaps at page edges.

How does the feed stay fresh without stampedes?

New articles are pushed into the precomputed feed as they arrive; nothing expires.

During a breaking-news spike, what limits the regional feed cache?

A region's feed is a few MB; the challenge is requests per second, solved with replicas and the CDN.

What makes polling 50,000 feeds cheap?

Unchanged feeds answer 304 with no body.
ResourceWhy
HTTP conditional requests (RFC 9110)ETags and If-Modified-Since, which make frequent polling cheap.
Design Google News (Hello Interview)Another walkthrough.