Product Manager Interviews Module 5 – Execution, prioritisation and technical rounds
System design for product managers
Lead a system design conversation as a product manager: write the requirements, sketch the parts, estimate the load and weigh fresh against fast data.
What you will learn
- Write the requirements that shape a design, with scale, latency, freshness and reliability as numbers
- Sketch the main parts of the system behind a product feature and the paths data takes between them
- Estimate the load a feature puts on a system and where it breaks first
- Weigh freshness, speed, consistency and cost with engineers, screen by screen
Before you start
What you’ll learn · Before you start
What you will learn
- Write the requirements that shape a design, with scale, latency, freshness and reliability as numbers
- Sketch the main parts of the system behind a product feature and the paths data takes between them
- Estimate the load a feature puts on a system and where it breaks first
- Weigh freshness, speed, consistency and cost with engineers, screen by screen
Before you start
On this page
A recipe page that shows its reviews looks like one small feature, yet at dinner time it can mean 20,000 reads a second for a database that serves 5,000. System design questions for product managers check that you can see this before anything is built: turn a feature into requirements, sketch the parts that serve it, estimate where it breaks first and decide with engineers what each screen can give up. Building it is the engineers’ depth, which the system design track covers.
Below, you design ratings and reviews for an invented recipe app: its requirements, its parts step by step, its load in a program and a model, and its trade-offs.
Requirements come first: how many, how fast, how fresh, how reliable
Before drawing a box, write the numbers that decide the design. Google’s SRE book calls a target for a measured indicator an SLO (service level objective): A target for a measured indicator of a service, such as 99 % of reads completing within 200 ms. The indicator itself is the SLI; an SLA adds consequences for missing the target., for example that 99 % of requests finish in under 100 ms. For the recipe reviews:
| Requirement | For recipe reviews | What it decides |
|---|---|---|
| Scale | 24 million users a day, 6 recipe pages each, busiest at dinner | Reads dominate, so a cache comes first |
| Latency | 99 % of review reads within 200 ms | Reads come from memory, not from the database |
| Freshness | A new review appears within 5 minutes | Checks can wait in a queue, out of the cook’s way |
| Reliability | 99.9 % of page reads succeed; posting may wait | Reading and writing can fail separately |
Each line rules options out: at the dinner peak, the 200 ms target rules out reading the database for every page, because it serves 5,000 of the 20,000 reads a second, and a 5-minute freshness rule lets every review be checked before anyone sees it.
Checkpoint
Check: Requirements as numbers
Most features are built from the same few parts
A product manager sketches the parts and the paths data takes between them, not the code inside them. Most features use the same kinds of part: the app, an API, a service that owns the feature’s data, a database that keeps it, a cache that keeps fast copies, and a queue that holds work for later. Step through one read and one write:
The drawing shows the whole scene; its 9 steps are listed below.
Every step as text
- A recipe page opens. A cook opens recipe 77. The app asks the API for its rating and its first reviews, and the API passes the request to the reviews service, the part that owns reviews.
- The cache first. The service asks the cache, a fast copy of recent answers held in memory. At a hit ratio of 90 %, nine reads in ten end here and never reach the database.
- A miss reads the database. This read misses, so the service reads the reviews database, the lasting record of every review, and stores the answer in the cache for the next reads of recipe 77.
- The page fills in. The answer travels back through the API to the app, which draws the stars and the first reviews.
- A review is written. Another cook posts a review with a photo. The photo goes straight to the photo store, which keeps large files apart from the reviews database.
- Saved, not yet shown. The review's text reaches the service, which saves it as pending. A pending review is kept safe but no reader sees it yet.
- In line for a check. The service puts the review in the moderation queue and answers the cook at once. The queue lets checks run at their own pace, so a burst of reviews never slows the page.
- The check. A worker takes the review from the queue and checks it for spam and abuse, flagging the rare doubtful one for a person.
- Approved, and the copy cleared. The worker marks the review approved and clears the cache's copy of recipe 77, so the next read misses, reads the database and shows the new review.
Each part answers a product question: the database says where the data lives, the cache what may be old, the queue what may be late. Ask which part owns each piece of data, and what the feature does when that part is slow.
Checkpoint
Check: The parts behind a feature
A load estimate shows where a design breaks first
Estimate before you choose parts. This program works out the dinner peak from stated assumptions:
# Load and storage for ratings and reviews in an invented recipe app. Every
# input is a stated assumption: change one, run it again, see what moves.
DAILY_USERS = 24_000_000
PAGES_PER_USER = 6 # recipe pages a user opens a day
READS_PER_PAGE = 4 # rating, first reviews, reviewers, photo list
PEAK_FACTOR = 3 # the dinner hour runs at 3 times the average
HIT_RATIO = 0.90 # share of reads the cache answers
DB_LIMIT = 5_000 # reads a second the database can serve
REVIEWS_PER_DAY = 50_000
PHOTO_SHARE = 0.2 # reviews that carry a photo
PHOTO_BYTES = 400_000 # one stored photo
TEXT_BYTES = 1_000 # one review's text and details
CHECK_SECONDS = 120 # average wait for a review's moderation check
DAY = 24 * 60 * 60
reads = DAILY_USERS * PAGES_PER_USER * READS_PER_PAGE / DAY * PEAK_FACTOR
to_db = reads * (1 - HIT_RATIO)
needed = 1 - DB_LIMIT / reads
print(f"peak reads: {reads:,.0f} a second")
print(f"reaching the database: {to_db:,.0f} (limit {DB_LIMIT:,})")
print(f"hit ratio needed: {needed * 100:.1f} %")
writes = REVIEWS_PER_DAY / DAY * PEAK_FACTOR
print(f"peak writes: {writes:.1f} reviews a second")
# Little's law: reviews waiting = arrival rate x average wait.
print(f"waiting for a check at peak: {writes * CHECK_SECONDS:.0f}")
GB = 1_000_000_000
text = REVIEWS_PER_DAY * TEXT_BYTES * 365 / GB
photos = REVIEWS_PER_DAY * PHOTO_SHARE * PHOTO_BYTES * 365 / GB
print(f"storage a year: text {text:,.0f} GB, photos {photos:,.0f} GB") Output
peak reads: 20,000 a second reaching the database: 2,000 (limit 5,000) hit ratio needed: 75.0 % peak writes: 1.7 reviews a second waiting for a check at peak: 208 storage a year: text 18 GB, photos 1,460 GB
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 capacity.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
Writes are easy at 1.7 reviews a second, and Little's law: The average number of people or requests in a system equals the rate at which they arrive times the average time each one stays, L = λW: 500 arrivals a minute staying 4 minutes each means 2,000 in the system at once. More in “Usage, revenue and capacity estimates” puts 208 reviews in the check queue at peak. Reads are the problem: 20,000 a second against a database limit of 5,000. Photos, not text, decide the storage, at 1,460 GB a year against 18 GB. Start the model at the program’s numbers, then drag the hit ratio down:
At 90 % hits, 2,000 of 20,000 reads a second reach the database, within its limit of 5,000. Every extra nine of hit ratio cuts the database load tenfold.
The numbers as a table
| Hit ratio | Database reads a second |
|---|---|
| 50 % | 10,000 |
| 80 % | 4,000 |
| 90 % | 2,000 |
| 95 % | 1,000 |
| 99 % | 200 |
| 99.9 % | 20 |
Below a cache hit ratio: The share of reads that a cache answers from its copies. Every miss goes on to the database, so at 20,000 reads a second a hit ratio of 90 % leaves 2,000 a second for the database. of 75 %, the database passes its limit. A cache that restarts empty starts near 0 %, so at dinner the first part to break is the database behind a cold cache.
Key idea
Estimate the peak before choosing parts: here reads, not writes, shape the design, and the cache’s hit ratio decides whether the database holds.
Checkpoint
Check: Where a design breaks first
Fresh, fast, consistent or cheap: what each screen gives up
Every design gives something up, and the product manager says what each screen can give up. Three trade-offs come up in almost every design conversation:
| Trade-off | The choice for recipe reviews | The product question |
|---|---|---|
| Fresh or fast | Reviews from the cache, up to 5 minutes old, or from the database every time | How old may this screen’s data be? |
| Consistent or available | When parts cannot reach each other, show the last copy or refuse | Which screens may show old data, and which must refuse? |
| Sharp or cheap | Keep photos as uploaded, or spend a week resizing them to a quarter of the size | Is a sharper photo worth four times the storage? |
Eric Brewer, who proposed the CAP theorem behind the second trade-off, writes that designers must choose between consistency and availability only “when A break in the network that leaves parts of a system unable to reach each other, so that each part must act on what it alone knows. are present”, so most of the time a design keeps both.
Checkpoint
Check: What each screen gives up
Practise: estimate the load before you choose the parts
Write the program’s three estimates as functions, with sample tests:
Exercise · Easy · Python
Estimate peak load and the hit ratio a database needs
Write three functions in load.py. The first two return reads a second rounded to one decimal place; the third returns a share rounded to three.
peak_per_second(per_day, peak_factor) turns a daily count into the rate of the busiest hour: the day's average rate a second (a day has 86,400 seconds) times the peak factor.
database_reads(reads, hit_ratio) returns the reads a second that reach the database when the cache answers the share hit_ratio of them. It raises ValueError for a hit ratio below 0 or above 1.
hit_ratio_needed(reads, limit) returns the smallest hit ratio, as a share from 0 to 1 rounded to three decimal places, that keeps the database at or under limit reads a second; it is 0 when the database alone keeps up.
peak_per_second(576_000_000, 3) # 20000.0: the recipe app's peak reads
database_reads(20_000, 0.9) # 2000.0
hit_ratio_needed(20_000, 5_000) # 0.75
The sample tests use the lesson's numbers, a database that keeps up alone, the ends of the hit ratio and bad input.
Starter code · load.py
def peak_per_second(per_day, peak_factor):
"""Return the busiest hour's rate a second: per_day / 86,400 x peak_factor, rounded to 1 decimal place."""
# Your code here.
return 0.0
def database_reads(reads, hit_ratio):
"""Return the reads a second that miss the cache and reach the database, rounded to 1 decimal place.
Raises ValueError for a hit ratio below 0 or above 1.
"""
# Your code here.
return 0.0
def hit_ratio_needed(reads, limit):
"""Return the smallest hit ratio (a share from 0 to 1) that keeps the database at or under limit, to 3 places."""
# Your code here.
return 0.0 The sample tests · test_load.py
from load import database_reads, hit_ratio_needed, peak_per_second
def test_recipe_app_peak():
"""24 million users x 6 pages x 4 reads a day, at 3 times the average"""
assert peak_per_second(24_000_000 * 6 * 4, 3) == 20000.0
def test_peak_rounds():
"""1,000,000 a day at a peak factor of 3 is 34.72 a second, rounded to 34.7"""
assert peak_per_second(1_000_000, 3) == 34.7
def test_reads_at_ninety_percent():
"""nine reads in ten end at the cache"""
assert database_reads(20_000, 0.9) == 2000.0
def test_ends_of_the_hit_ratio():
"""an empty cache sends every read on; a perfect one sends none"""
assert database_reads(20_000, 0) == 20000.0
assert database_reads(20_000, 1) == 0.0
def test_hit_ratio_below_zero():
"""a share cannot be negative"""
try:
database_reads(20_000, -0.1)
except ValueError:
pass
else:
assert False, "expected a ValueError for a hit ratio of -0.1"
def test_hit_ratio_above_one():
"""90 is a percentage, not a share"""
try:
database_reads(20_000, 90)
except ValueError:
pass
else:
assert False, "expected a ValueError for a hit ratio of 90"
def test_ratio_needed_for_the_limit():
"""5,000 of 20,000 reads may reach the database"""
assert hit_ratio_needed(20_000, 5_000) == 0.75
def test_ratio_needed_rounds():
"""5,000 of 30,000 may reach the database: 1 - 1/6 = 0.8333…, rounded to 0.833"""
assert hit_ratio_needed(30_000, 5_000) == 0.833
def test_database_keeps_up_alone():
"""4,000 reads a second fit under a limit of 5,000 with no cache at all"""
assert hit_ratio_needed(4_000, 5_000) == 0.0 A hint
Every miss reaches the database, so the database's share of the reads is 1 - hit_ratio. Turn that round for the third function: the database may take limit / reads of the reads, so the cache must answer the rest. Round each result at the end, with round(x, 1) for reads and round(x, 3) for a share, so that 20,000 reads at a hit ratio of 0.9 give 2000.0 and not 1999.9999999999995.
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
Then the self-check below begins where your answers so far are weakest.
Practice that adapts to you
Practise this lesson
12 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Two features designed at a product manager's depth Pro: opens with a Pro, Premium or Ultimate pass
Two features of other invented apps, designed the way a product manager leads the conversation: the requirements, the load in numbers, the parts, how they fail and the decisions each forces. The numbers come from one program you can run and change.
In this part:
- Seat holds on a festival route: 2,000 seats, 30,000 riders, one second that matters
- Live scores in a final: 2 million phones, one number
- The questions to ask engineering, for both designs
- What the two designs share
1 diagram · 1 runnable example (Python) · about 6 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
The product rules a design needs Pro: opens with a Pro, Premium or Ultimate pass
A design is finished only when it says what users see when a part is slow, old or down. Engineers can build any of these behaviours; choosing between them is product work.
In this part:
- Staleness limits, retries and failure screens, in one table
- Setting each limit from the cost of being wrong, with numbers
- Retries the user sees, and the retries that make an outage worse
- A failure drill: the reviews database down for 10 minutes at dinner
- What each level adds to a system design answer
1 runnable example (Python) · about 5 minutes
Opening this part with your pass…Pro part. It opens with the same pass as the part above.
Interview questions
For each, say the requirement that decides the design, the number you would estimate first and the product rule it needs, before you open the first step and the follow-ups:
-
Mid to staff
Design live order tracking for a delivery app at a product manager's depth: requirements, main parts and the trade-offs you would raise.
-
Mid to senior
Your app must send 10 million notifications within an hour for a sale. What would you ask engineering, and which product rules would you set?
-
Fresher to senior
Prices in your app can be up to 5 minutes old because of caching. When is that acceptable, and when is it not?
Flashcards · 7 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
Which four requirements should a product manager write as numbers before a design?
Scale (how many users and requests, and when they peak), latency (such as 99 % of reads within 200 ms), freshness (how soon new data must appear) and reliability (what share of requests must succeed). Each line rules options out.
-
What is an SLO?
A service level objective: a target for a measured indicator, for example 99 % of requests finishing in under 100 ms.
-
Which common parts make up most product features, and which product question does each answer?
An app, an API, a service that owns the feature's data, a database, a cache and a queue. The database says where the data lives, the cache what may be old, and the queue what may be late.
-
In the recipe-reviews example, what shapes the design: reads or writes?
Reads: 20,000 a second at the dinner peak against a database that serves 5,000, while writes are only about 1.7 reviews a second. Photos, not text, decide the storage.
-
Why does a cache that restarts empty endanger the database?
With 20,000 reads a second and a database limit of 5,000, at least 75 % of reads must hit the cache. An empty cache starts near 0 %, so at the peak the database is the first part to break.
-
Which three trade-offs come up in almost every design conversation?
Fresh or fast: how old may this screen's data be? Consistent or available: when parts cannot reach each other, show the last copy or refuse? Sharp or cheap: is a sharper photo worth four times the storage?
-
When does a design have to choose between consistency and availability?
Only while a network partition is present; the rest of the time a design can keep both.
Key takeaways
- Requirements come first: scale, latency, freshness and reliability, each as a number, decide which parts a design needs.
- Most features use the same parts, an app, an API, a service, a database, a cache and a queue, joined by read and write paths.
- A load estimate shows where a design breaks first; for recipe reviews it is the database once the cache’s hit ratio falls below 75 %.
- Every design trades freshness, speed, consistency or cost, and the product manager says what each screen gives up.
Terms in this lesson
- cache hit ratio
- The share of reads that a cache answers from its copies. Every miss goes on to the database, so at 20,000 reads a second a hit ratio of 90 % leaves 2,000 a second for the database.
- SLO (service level objective)
- A target for a measured indicator of a service, such as 99 % of reads completing within 200 ms. The indicator itself is the SLI; an SLA adds consequences for missing the target.
References
- Site Reliability Engineering, chapter 4: Service Level Objectives (Google (O'Reilly Media))
- Site Reliability Engineering, chapter 22: Addressing Cascading Failures (Google (O'Reilly Media))
- A Proof for the Queuing Formula: L = λW (Operations Research 9(3)) (INFORMS (John D. C. Little))
- CAP Twelve Years Later: How the 'Rules' Have Changed (IEEE Computer 45(2)) (IEEE Computer Society (Eric Brewer))
- RFC 9110: HTTP Semantics (IETF (RFC Editor))
Report a problem with this lesson
Kept only in this browser. Your Learn progress