Design ad frequency capping
Company: Netflix
Role: Software Engineer
Category: System Design
Difficulty: medium
Interview Round: Onsite
## Design an Ad Frequency Capping System
Design a **frequency capping** system for an advertising platform. The system must ensure that a user does not see the same advertisement more than a configured number of times within a given time window. Representative limits include:
- at most **3 impressions** per user per ad per day
- at most **10 impressions** per user per campaign per week
Your design should address each of the following:
- how the ad-serving system checks caps **in real time**, before an ad is shown
- how impressions are **counted and stored**
- how to support multiple **cap scopes**, such as user-ad, user-campaign, or household-level limits
- how to handle **high QPS, low latency, and eventual consistency** across regions
- what happens when counting data is **delayed, duplicated, or arrives out of order**
- **data retention**, expiration of old counts, and the operational trade-offs between accuracy and latency
```hint Where to start
There are two very different workloads hiding in this prompt: deciding whether an ad may be shown, and recording that it was shown. They have opposite performance profiles — one is latency-critical and on the request path, the other is high-volume and can tolerate slack. Notice the tension before committing to a single store or pipeline for both.
```
### Clarifying Questions to Ask
- What is the expected **ad-serving QPS** and the read-latency budget per cap check (e.g. p99 under a few ms)?
- How many **distinct cap scopes** must be supported per ad request (user-ad, user-campaign, household), and can several caps apply to a single candidate ad simultaneously?
- Are the time windows **calendar-aligned** (per UTC/local day, per ISO week) or **rolling** (last 24 hours)? Does timezone matter?
- How **strict** must enforcement be — is a small, bounded overshoot acceptable, or must the cap be exact even under concurrency and across regions?
- Is serving **single-region or global**, and what is the tolerance for cross-region inconsistency?
- On a counter-store outage, should the system **fail open** (keep serving, risk violations) or **fail closed** (stop serving, lose revenue)? Is this configurable per campaign?
### Constraints & Assumptions
- **Scale:** assume a large ad platform — high ad-serving QPS with a tight per-check latency budget (single-digit milliseconds), and a high-volume impression event stream.
- **Caps:** multiple configurable limits per scope (user-ad, user-campaign, user-advertiser, household-campaign) over hourly / daily / weekly / rolling windows.
- **Correctness target:** reasonable accuracy despite duplicate, delayed, or out-of-order events; a small bounded overshoot is acceptable by default unless a campaign opts into stricter enforcement.
- **Availability:** the serving path must stay highly available; the system spans multiple regions.
```hint The crux to reach
Once read and write are separated, the central judgment call is *how fresh the counts on the decision path need to be*. Be explicit about where you'd tolerate a stale count (and therefore a bounded violation) versus where correctness is worth extra cost — and let the per-campaign strictness requirement, concurrency, and the multi-region picture drive that decision rather than a single global stance.
```
### What a Strong Answer Covers
These are the **dimensions an interviewer evaluates** — signals to look for, not a prescribed design:
- **Path separation:** whether the candidate recognizes the decision path and the impression-recording path as distinct concerns with different latency/throughput needs, rather than conflating them.
- **Data model:** whether cap rules are modeled in a way that supports several scopes and window types, and whether the candidate proposes a counting key that distinguishes *scope* and *time window* (regardless of the exact encoding).
- **Read path:** whether the candidate keeps the per-request eligibility check cheap and bounded when multiple caps apply to multiple candidate ads.
- **Write path:** whether impressions are recorded durably and reliably feed whatever maintains the counts.
- **Consistency trade-offs:** whether the candidate reasons explicitly about freshness vs. latency/cost and matches different strategies to different strictness requirements, instead of asserting one approach everywhere.
- **Reliability:** whether the candidate addresses duplicate, delayed, and out-of-order events, and safe behavior under concurrent requests for the same user.
- **Lifecycle:** whether short-lived counts and longer-lived raw event history are treated differently, with sensible expiration.
- **Scale & failure:** whether the candidate handles partitioning, hot keys, multi-region enforcement, pipeline lag, store outages, and the observability needed to detect overshoot.
### Follow-up Questions
- How does the design change at **100x scale** or when a single campaign or user becomes a **hot key** that bottlenecks one counter shard?
- A campaign requires **exact** enforcement (no overshoot, ever). What do you add, and what does it cost in latency and complexity?
- The aggregation pipeline **falls hours behind**. How do you detect it, how do serving decisions degrade, and how do you recover the correct counts?
- An ad is **selected but never actually rendered** (user scrolls past, page abandoned). How do you avoid over-counting impressions that were reserved but not shown?
Overview: This question evaluates a candidate's competence in large-scale system design, including distributed systems, real-time analytics, data modeling, and consistency and scalability trade-offs for ad frequency capping.
Community answers
Answer by saurabhramya
User
Compaign
ad
one compaign may have multiple ads
10 impressions per compaign per user per week
3 impressions per day per user
user id -> compaign - count - from date - to date
ad -> ad count
ad -> getad(userid) api
so let's create the mvp.
user request comes to apigw and it gets routed to the ADService. The ADService app tier runs some logic to find the appropriate ad
and serve it by returning some reference and the client can go download the content of the ad and play it.
before we scale it lets think of the algorithm and object oriented design of it.
we will not discuss much about the USER today and will focus on the compaign and ad objects
one house hold -> multiple users
one campaign -> active, multiple ads
one ad -> detail
Count table
Household -> user 1 -> ad 1 -> Campaign 1 - count - date
-> user 2 -> ad 1 -> Campaign 2 - count - date
this will support the queries like how many and which ads ran per campaign across multiple scopes in a particular date range?
so when a client asks for an ad the it can run through the db and check which one still has some quota to run it might return that.
the algo might do some random choices or some kind of round robin between the not-yet-over ads of active campaign.
in mvp...when the read request comes then the app tier looks for it in the cache tier and if it does not find it then it goes to the db reads it...find the result and store that in the cache and returns the ad id to the client.
we will talk about what does it store in t
Answer by sharmanikhil610
Solution: https://imgur.com/a/oO5cEFK