Think like a designer / CHAPTER 03
Functional and non-functional requirements
Translate a product idea into observable operations, quality targets, and correctness rules.
Builds on What a system design interview measures.
- Separate product behavior from quality constraints.
- Write measurable targets with explicit boundaries.
- Identify invariants and priority conflicts.
01Turn a product idea into three kinds of promise
Noor saves an article on her phone and wants to see it on her laptop. Write down three different kinds of promise:
- Functional requirements describe actions. Noor can save an article link, list saved links and remove one.
- Nonfunctional requirements describe measurable quality. For example, list requests must finish within a response-time target at a stated load.
- Invariants describe rules that must always hold. For example, concurrent saves of the same user–article pair must leave one active entry.
Also decide whether the saved object is only a link or a permanent copy of the article. Copying content requires file storage and deletion rules that link storage does not.
Ask questions that change the design:
- Must the laptop see a phone save on its next request, or may it briefly show the old list?
- Is the list private, or can Noor share it?
- If protected article content is displayed, what happens after access is revoked?
- During an outage, may the service show an older list, or must it refuse the read?
Record the answers before choosing mechanisms. “Use a cache” proposes an implementation. “Return the list within 300 ms at the agreed load” states a need that might justify that implementation.
02Write targets that another engineer can test
For this exercise, use explicit requirements that another engineer can check.
Functional requirements:
- FR1 — Save: Save an article link for the authenticated user. Repeating the same user–article save resolves to one active entry.
- FR2 — List: List that user’s saved entries newest first, using the saved time and entry identifier to break ties. Return a cursor so another page continues that order.
- FR3 — Remove: Remove an entry owned by the caller. Later list reads from the database omit it; state separately how soon any cached list must reflect that removal.
Nonfunctional and correctness requirements:
- NFR1 — Latency: At 1,000 peak valid list reads/second, at least 99% finish within 300 ms from application receipt to completed response. Measure public-network delay separately.
- NFR2 — Success: At least 99.9% of valid list reads succeed over a month. Define whether client errors, timeouts and partial responses count before measuring.
- NFR3 — Uniqueness: At most one active entry exists for each user–article pair, including simultaneous saves and repeats.
- NFR4 — Privacy: Check the required owner/content permission on every private response. Cached bytes and a known identifier do not grant access.
The response-time requirement uses the 99th percentile, written p99. It constrains the measured requests near the slow end, not just their average. A fast average can hide a small slow group.
Keep these storage questions separate:
- Durability: Will the accepted save survive the stated failures?
- Freshness: Which later reads must include that save, and how soon?
One database copy can safely retain the save while another still serves the old list. Such a copy is called a replica. State the read requirement; durability alone does not answer it.
03Trace concurrency and resolve a conflict
Let the article be a8; the saved pair is (Noor, a8). Two devices can both read “not saved” before either inserts. Separate application checks do not prevent two rows.
Make the database enforce the rule:
- Store a key containing both user and article identifiers.
- Add the database’s appropriate uniqueness constraint for that pair.
- Each save attempts the insert through that constraint.
- One creates the entry. The other encounters the existing pair and returns that entry or the documented already-saved response.
The database must check uniqueness while accepting the insert, as one indivisible operation. This is an atomic check and update. Without it, another insert can occur between an application’s check and write.
Then handle an unavailable permission check deliberately:
- An old public title may be acceptable under the freshness policy.
- An old permission can expose protected content after access was removed.
- If the service cannot establish the required permission, it waits or denies as agreed; it must not return protected bytes just because a cached copy exists.
Write an acceptance check for each promise:
- Race two saves and verify one active pair.
- Run the stated read workload and measure the response-time distribution and failures.
- Revoke access while a cached copy remains, then verify that protected content is not returned.
- Save on the phone and read from the laptop to test the agreed visibility delay.
These tests exercise different requirements. Passing a response-time test does not prove uniqueness or privacy.
Two concurrent saves, one authoritative entry
Follow the numbered arrows
- Phone save → Database uniqueness ruletries to create pair
- Laptop save → Database uniqueness ruletries same pair
- Database uniqueness rule → Saved entryone creation wins
- Database uniqueness rule → Second saveduplicate resolves to existing
Trace the diagram in words
- Both devices submit the same pair.
- The guard permits one active entry.
- The second request receives the existing outcome instead of another entry.
DRAW. CALCULATE. EXPLAIN.
Write an acceptance contract and catch a race
20 minutes · guided practice
Assume 1,000 peak list reads/second, p99 below 300 ms and one active entry per user–article pair. Two devices both observe that an article is not saved.
- Explain how separate checks can produce duplicates.
- Show the database operation that prevents them.
- Write separate checks for uniqueness, speed and cached-content privacy.
Compare the worked solution
- Both devices can finish their existence check before either insert, so both may try to create a row.
- Enforce a unique user–article pair in the database. One insert succeeds; the other returns the existing entry or the defined conflict. The check and accepted insert must occur together.
- Race simultaneous saves and verify one active pair.
- Measure list requests from application receipt to response under the stated peak. Record timeouts and errors rather than excluding them from the evidence.
- Leave article bytes cached, revoke the required access, and verify that a later unauthorized response contains no protected content.
The checkboxes record your own assessment. The worked solution gives you a way to check the reasoning.
RETRIEVE BEFORE YOU REVEAL
Keep the important ideas.
Say the answer aloud, then turn the card over.
CARD 01Which three kinds of promise should requirements distinguish?
- A functional requirement names a user action, such as saving an article.
- A quality target measures that action, such as
p99response time. - An invariant is a rule that must hold despite races, such as one active entry per user–article pair.
CARD 02How do I make a target testable?
- Name the operation and expected load.
- Define timing start and end, or which outcomes count as success.
- State the required threshold and measurement period.
- Explain what users see if a required check cannot finish.
CARD 03How can a durable save still be missing from a read?
- The writer safely retains a save.
- Another replica may still hold the older list.
- A laptop reading that replica can miss the save.
Define when the laptop must see it and choose a source that can meet that visibility requirement.
CHECK THE REASONING
Can you explain the choice?
Answer every question, then finish for an immediate score and an explanation for each answer. Aim for 80%, then revisit any gaps.
CLOSE THE LOOP
Explain it without the page.
Summarise the mechanism, one trade-off, and one failure mode. Then mark this chapter complete.