Functional scope
Turn a product requirement into a workload model, measurable objectives and a justified initial capacity estimate.
THE IDEA
From a wish to operations
"Build a social photo app" describes a feeling, not a system. You cannot size servers, pick a database or write a test for a feeling. A system is defined by the operations its users perform and by what counts as success for each one. An operation has an actor, an input, a result and a read or write nature. Our case study, a made-up service called Pixelfold, turns the wish into things like:
Vague: "Users share photos with friends"
Concrete: Upload a photo -> stored durably, on my profile within 5 s, I get its id
Load my feed -> newest 20 photos from accounts I follow, newest first
Open a photo -> full resolution for anyone allowed to see it
Follow a user -> their new photos show in my feed, and I can undo itThe split into reads and writes matters early, because they scale differently. Reads can often be copied and cached. Writes must be ordered, stored and made durable.
Scope is load
Every operation you promise brings an access rate with it. Switch operations on and off below and watch the read and write rates change. Adding direct messages is not a free checkbox: it adds the busiest write path in the table.
Tick the operations you promise to build. Each one carries an assumed number of actions per daily active user per day, so scope turns directly into load and storage work.
| In scope | Operation | Kind | per user per day | avg req/s at 200k DAU |
|---|---|---|---|---|
| Upload a photo | write | 0.5 | 1.16 | |
| Load my feed (recent photos from people I follow) | read | 25 | 57.87 | |
| Open a single photo | read | 6 | 13.89 | |
| Follow another user | write | 0.2 | 0.46 | |
| Like a photo | write | 8 | 18.52 | |
| Comment on a photo | write | 1.5 | 3.47 | |
| Search for users and tags | read | 3 | 6.94 | |
| Post a 24-hour story | write | 0.8 | 1.85 | |
| Upload short videos | write | 0.2 | 0.46 | |
| Send direct messages | write | 4 | 9.26 |
The per-user counts are assumptions for this made-up service. The point is the chain: a feature is an operation, an operation has a rate, and a rate is load.
Three kinds of statements
A requirements list mixes three different things and they should be kept apart. Functional statements say what users can do. Quality statements say how well, with a number: speed, availability, durability. Constraints are limits you did not choose: budget, people, existing platform, deadline. Mixing them hides decisions. "Fast and reliable" is neither functional nor measurable.
Predict first. Which statement specifies a read or write operation and its result?
The common mistake: a feature list with no success criteria
"The server returns 200" is a status, not success. An upload that returns 200 and then loses the photo is a failed upload. Write the criterion in terms the user would verify: the photo is stored, visible, and the id comes back.
Critical first: name the smallest set of operations without which the product is pointless, then add success criteria you can measure.
Ship every idea in the first release. Each extra operation brings storage, tests and on-call work, as the table above shows.
Chapter context
System design starts with what the system must do and the conditions under which it must do it. A component diagram is only useful when its choices can be traced back to access patterns, data volume, latency targets and failure expectations.
TRY IT YOURSELF
The founder says: "Build a social photo app." That is not yet a specification. First sort what you have been told, then decide what the first release must do and how you will know each operation worked.
Part 1: sort the statements
| A user can upload a photo and later see it on their profile. | |
| The feed loads in under 400 ms for 99% of requests. | |
| The team is two engineers and nobody is on call overnight. | |
| A user can follow another user. | |
| No upload that was acknowledged to the user may ever be lost. | |
| It must run in the existing cloud account with at most 4,000 dollars per month. | |
| A user can comment on a photo. | |
| The service is available for 99.9% of each month. |
Part 2: scope the first release (the team can build at most 6 operations)
| Operation | Decision | What counts as success |
|---|---|---|
| Upload a photo write, about 0.5 per user per day | - | |
| Load my feed (recent photos from people I follow) read, about 25 per user per day | - | |
| Open a single photo read, about 6 per user per day | - | |
| Follow another user write, about 0.2 per user per day | - | |
| Like a photo write, about 8 per user per day | - | |
| Comment on a photo write, about 1.5 per user per day | - | |
| Search for users and tags read, about 3 per user per day | - | |
| Post a 24-hour story write, about 0.8 per user per day | - | |
| Upload short videos write, about 0.2 per user per day | - | |
| Send direct messages write, about 4 per user per day | - |
Checks (0 of 5)
- Sort all 8 requirement statements into functional, quality and constraint correctly
- Decide every candidate operation: MVP or later
- Every operation the product cannot work without is in the MVP
- The MVP holds 4 to 6 operations (the team can build and run no more)
- Every MVP operation has a measurable success criterion
Stuck, or something not working? You can come back to it any time.
CHECK YOUR UNDERSTANDING
Does saying build a social app specify its read and write operations?
Chapter reference: math, worked example & code
This section belongs to the whole chapter, so it is the same on every lesson in it and may cover a different lesson's topic. The code for the experiment you just ran is inside the experiment itself.
Math and notation
For U daily active users and a requests per user per day:
Here p is a peak multiplier, c measured capacity per instance and u a chosen usable fraction. The multiplier and capacity are workload assumptions, not universal constants.
Worked example
Assume 100,000 daily users, 20 requests each and a peak multiplier of 10. Average demand is 2,000,000/86,400≈23.15 requests/s and estimated peak is 231.48 requests/s. An instance sustaining 100 requests/s with usable fraction 0.6 contributes 60 requests/s, so four instances cover the estimated peak. Five are needed to retain that same usable capacity after losing one instance.
Reference code
The following example uses the Python standard library and does not call a remote service.
from math import ceil,isclose
average=100_000*20/86400
peak=10*average
usable=100*.6
assert isclose(average,23.14814814814815)
assert ceil(peak/usable) == 4
assert (5-1)*usable >= peak
assert (4-1)*usable < peak
Python examples are provided for study and local execution; this page does not execute Python.
Common mistakes
Average traffic is not peak traffic. A headroom assumption is not measured performance. Durability, correctness and availability need separate objectives.