Module 01 · Lesson 5 of 14Free
Functional and non-functional requirements
- Level
- Beginner
- Topic
- System design fundamentals
- Reading time
- 15 min
When you finish this page you can
- Distinguish a feature’s behavior from its quality targets and constraints.
- Turn vague wishes into measurable indicators and objectives.
- Calculate what monthly reliability targets allow.
- Resolve conflicting requirements through explicit product decisions.
Builds on 1 lesson
Start with lesson 414 minVideo lesson · coming soon
adds order tracking before its festival sale. A customer opens her order and waits 8 seconds to see “Shipped”.
The feature passes its test: it displays the right status. The customer still thinks it is broken.
What must we specify beyond “customers can track orders” to build the right feature?
A working feature can still fail
Start with two statements:
- A customer can view the shipping status of her own order.
- The tracking response arrives within 300 ms for at least 95% of eligible requests.
The first describes what the system does: a . The second describes how well it must do it: a .
Non-functional does not mean optional. Both belong in the design’s acceptance criteria, rather than being left for implementation teams to guess. Microsoft’s architecture guidance similarly connects functional and non-functional choices to business needs.3
Kartly always returns the correct status, but every response takes 8 seconds. Has it met the requirement “customers can track their orders”?Show answer
Diagram not drawn yet: kartly-tracking-requirement-chain.svg
Three connected boxes: customer problem, waiting without useful information; required behavior, view own order status; acceptance evidence, response timing and update visibility. Arrows connect the problem to behavior and behavior to evidence.
For this lesson, Kartly still has one app server and one PostgreSQL database. Tracking shows shipment status, not a live courier map.
The initial behavior is small:
| Product question | Agreed behavior |
|---|---|
| What can a customer see? | The latest confirmed shipping status and its update time |
| Whose orders can she see? | Only orders belonging to her account |
| Who changes the status? | Authorised fulfilment staff |
| What happens after a failed refresh? | Preserve the previous display and label it as not refreshed |
The ownership rule describes behavior and also supports privacy. The categories can overlap. Their purpose is to reveal missing requirements, not to force every sentence into one category.
We now know what tracking does. We still need to define acceptable service.
“Fast” needs a measurement boundary
“Make tracking fast” leaves several questions unanswered. Does timing start when the customer taps, when the server receives the request, or when a database query starts?
As Latency and throughput established, the boundary changes the number.
A , or SLI, measures an aspect of service quality. A , or SLO, gives that indicator a target over a stated period. A useful indicator is often good events divided by eligible events.1
Here is Kartly’s proposed tracking agreement.
| Wish | Indicator | Proposed objective |
|---|---|---|
| “It opens quickly” | Fraction of eligible reads returning a valid status within 300 ms | At least 95% each calendar month |
| “It works when needed” | Fraction of eligible reads returning a valid status within 2 seconds | At least 99.9% each calendar month |
| “Updates appear promptly” | Fraction of observed committed changes displayed within 30 seconds | At least 99% each calendar month |
These are illustrative product targets, not measured Kartly performance or universal recommendations.
Define eligible reads as authenticated requests for an existing order owned by the requester. Time starts when the request reaches Kartly’s public entry point and ends when its response leaves.
and server errors count as bad events. A quick error does not count as a fast, valid status. Invalid requests and deliberate attempts to read someone else’s order are excluded from these service indicators.
The update objective has a different boundary. It starts when PostgreSQL confirms a status change. It applies to customers already viewing that order in an online, foreground app. An expected update that misses its deadline counts as bad, not as a missing sample.
This objective does not promise that Kartly knows about a parcel within 30 seconds of physical movement. That would require a separate agreement about how staff record shipment events.
Measurement boundaries expose promises that the system cannot make.
One extra nine removes most allowance
An is the bad-event allowance implied by an SLO. A 99.9% success objective leaves 0.1% for bad events. It is a planning limit, not permission to ignore failures.1
People often translate percentages into minutes. That is valid for a time-based measure: available time divided by total time. Request-based availability uses a different denominator.2
For the following calculation only, assume a time-based objective and an exact 30-day month.
Find the whole observation period
30 days × 24 hours/day × 60 minutes/hour = 43,200 minutes
Use the actual month length for a calendar-month report.
Calculate the 99.9% allowance
43,200 minutes × 0.001 = 43.2 minutes
That allows 43 minutes and 12 seconds of total unavailability.
Calculate the 99.99% allowance
43,200 minutes × 0.0001 = 4.32 minutes
That allows 4 minutes and 19.2 seconds. The extra nine makes the allowance ten times smaller.
Under this time-based example, a 6-minute restart exceeds the entire 99.99% allowance. It does not exceed the 99.9% allowance by itself.
But Kartly’s proposed objective counts requests. In an illustrative month with one million eligible reads, 99.9% allows 1,000 bad reads. At 99.99%, the allowance is 100.
Do not describe those request budgets as guaranteed downtime allowances. A short interruption during the festival sale can affect more requests than a longer interruption overnight.
Higher availability targets also require more demanding recovery and operating practices. Buying another server alone does not establish a service guarantee.2
For Kartly, the consequence is a question for the product owner: does reducing the allowance justify the additional engineering work?
Targets choose the refresh behavior
The simplest implementation reads the status when the customer opens the page. The app server checks ownership, reads PostgreSQL, and returns the status.
That satisfies the initial display behavior. It cannot satisfy the update objective if the customer leaves the page open.
A small improvement is : the app asks for the current status repeatedly. The question becomes how often.
How should Kartly refresh an open tracking page?
Read once when the page opens.
The page can show an old status indefinitely. This works only if customers are expected to refresh manually.
Poll every 20 seconds while the page is visible.
The app normally discovers a change at its next scheduled read. It reuses the existing request path, but repeated reads still cost work.
Push each update to the app.
The server sends changes over a long-lived connection. Updates can arrive sooner, but reconnecting and recovering missed changes need additional behavior.
Why this choice
Choose polling provisionally because the target allows some delay and the existing read path already works. The 20-second interval leaves part of the 30-second target for requests and rendering.
This is not a guarantee: slow networks and delayed timers can still miss the deadline. Validate the choice with measurements. Reconsider push if customers need much faster updates or repeated reads become too costly.
Diagram not drawn yet: kartly-tracking-refresh-deadline.svg
A timeline starts with a poll at zero seconds. A status change commits just afterward. The next scheduled poll starts at 20 seconds. Request and rendering time follow, with a separate deadline marker at 30 seconds. A delayed poll crosses the deadline and is labelled missed target.
For a worked example, assume a change commits just after a poll. The next poll starts about 20 seconds later. Suppose its request and rendering take another second. The customer sees the change after about 21 seconds.
A 30-second interval leaves almost no room for that extra work. Setting an interval equal to the deadline confuses one component’s timing with the whole experience.
Server concurrency explains how the server handles overlapping reads. We will estimate their volume in Capacity estimation.
Freshness has changed client behavior. The failure objective changes what happens when refreshing cannot succeed.
A fallback can hide a failure
Suppose a refresh cannot reach PostgreSQL. The app already displays an earlier status.
Showing that status is useful, but calling it current would mislead the customer.
Should an old tracking display count as a successful refresh?
Count any displayed status as success.
The screen stays useful, but the metric can report success while customers never receive updates.
Separate a useful fallback from a successful refresh.
Keep the previous status visible with a warning. Count the failed request as bad for the read objective.
Clear the display whenever refreshing fails.
The customer loses even the information already available. This may be necessary for sensitive information, but shipment status does not require it here.
Why this choice
Kartly chooses an honest fallback. The app preserves its previous display, shows “Could not refresh”, and retains the last successful check time.
After a later read succeeds, the app replaces the status and clears the warning. If the product later requires confirmed-current information before an action, disable that action until verification succeeds.
This choice preserves information without changing the meaning of success. Google’s SLO guidance similarly distinguishes degraded service from an undegraded result.1
The privacy constraint changes a third implementation detail. The server checks ownership on every read, even when an order identifier appears difficult to guess.
If ownership cannot be verified, it returns no order information. On logout, the app clears its retained tracking display.
For this scenario, privacy wins over returning a status. It is a required rule, not something Kartly may violate within a monthly allowance.
Requirements become useful when they say what to sacrifice during a conflict.
Conflicts need an explicit owner
Suppose the team proposes two launch requirements:
- Every tracking view must confirm the latest status.
- Tracking must remain usable when the database cannot be reached.
If PostgreSQL is the only source of current status, the system cannot meet both during a database outage.
Do not hide that conflict behind “best effort”. Present the product owner with the consequences:
| Choice | Customer consequence |
|---|---|
| Require a fresh database read | Tracking can fail during an outage |
| Permit an explicitly old display | Customers keep earlier information, but it may be outdated |
| Add another serving path | More recovery behavior must be designed and verified |
For this lesson, the product owner accepts the labelled old display. Current verification remains unavailable during the outage.
Record the decision, its owner and its reconsideration condition. Here, reconsider if customers need tracking information to make a time-sensitive delivery choice.
Negotiation and clear documentation are part of architecture work, not evidence that requirements have failed. Microsoft’s guidance describes requirements refinement as an iterative, collaborative process.3
Evidence turns targets into decisions
Before launch, write acceptance checks from the agreement:
- Delay a tracking response beyond its deadline.
- Commit a change immediately after a poll.
- Interrupt database access during a refresh.
- Request another customer’s order.
- Log out after displaying an order.
For timing, begin with controlled test clients. Compare the last observed status with the committed change, and record whether the deadline was met.
In production, entry-point measurements cover Kartly’s response boundary, not the whole phone experience. Client measurements can reveal failures outside that boundary, but introduce their own coverage limitations.1
Keep the objectives provisional until there is evidence. If measurements miss a target, investigate the cause before choosing between changing the design and negotiating the target.
Detailed operating policies belong in SLOs and error budgets.
Where this shows up
- Design: a fast product catalog page: acceptable staleness helps determine whether stored copies are useful.
- Design: a notification service: delivery deadlines must specify what counts as delivered.
Kartly now has explicit promises; next, it needs to estimate the traffic and storage required to support them.
Common mistakes
- Naming a technology as a requirement. “Use push” is a proposed solution. “Show updates within 30 seconds” leaves alternatives open.
- Reporting only an average. It does not establish that the agreed fraction of requests meets the deadline.
- Excluding inconvenient failures. A database timeout still affects the customer.
- Treating every rule as a percentage. Protecting another customer’s information is not an error-budget tradeoff.
Check your understanding
Kartly returns an error in 50 ms. Does this count as a good event for its 300 ms objective?Show answer
A time-based objective is 99.99% over an exact 30-day month. Does a 6-minute outage fit within the allowance?Show answer
The team changes polling to every 30 seconds. Can it now promise every committed change appears within 30 seconds?Show answer
The database is unavailable, but the app still shows yesterday’s status. Has Kartly met its successful-read objective?Show answer
Summary
- Specify both the feature’s behavior and measurable quality targets to define what “works” means.
- An SLI measures service quality; an SLO sets its target and observation period.
- In a time-based 30-day month, 99.9% allows 43.2 minutes unavailable; 99.99% allows 4.32 minutes.
- Freshness, failure and privacy requirements change refresh behavior, fallback rules and ownership checks.
- Resolve conflicts explicitly: name what loses, who accepts that consequence and when to reconsider.