FullStack Course LogoFullStack Course
Module: System Design
System Design·248·9 MIN READ

248: System Design Interview Method: Functional Requirements, Non-Functional Requirements, and Scope

TOPICS COVERED: System Design Interview Method: Functional Requirements, Non-Functional Requirements, and Scope

Learning outcomes

By the end of this lesson, you should be able to:

  • explain and apply functional requirements in a realistic implementation;
  • explain and apply non-functional requirements in a realistic implementation;
  • explain and apply scope and non-requirements in a realistic implementation;
  • explain and apply actors and trust boundaries in a realistic implementation;
  • explain and apply core entities in a realistic implementation.

Prerequisites and retrieval

This lesson assumes that you have completed the earlier 01–06 foundation and the preceding lessons in this module. Before you start, retrieve one concrete example from a previous project where you had to deal with the same kind of concern. That retrieval step is useful because the goal here is not to memorize a vocabulary list. The goal is to make a defensible design decision for a large-scale distributed service, where requirements, traffic, failure modes, cost, and operational constraints all need to be explicit.

Terminology

  • Functional requirements: Identify the core actions that users or systems must perform, along with the data flowing through those actions.
  • Non-functional requirements: Make expectations for latency, availability, durability, consistency, security, privacy, throughput, geographic reach, and cost explicit when they materially affect the design.
  • Scope and non-requirements: State what you are intentionally leaving out so the chosen part of the design can receive enough depth.
  • Actors and trust boundaries: Identify users, internal services, third parties, operators, and automated clients, and distinguish where trust changes between them.
  • Core entities: Name the important data objects and their relationships before choosing APIs or databases.
  • Success criteria: Define what “good” looks like with measurable goals, or at least directional targets, so later trade-offs can be justified rather than described as universally scalable.

Mental model

Treat System Design Interview Method: Functional Requirements, Non-Functional Requirements, and Scope as a design problem with observable inputs, outputs, invariants, and failure modes. Before discussing queues, caches, or databases, decide what problem the system is actually solving. Requirements and explicit exclusions constrain every later decision more strongly than fashionable architecture choices do.

A strong implementation makes its assumptions visible, narrows uncertainty at system boundaries, and leaves evidence that explains why the design is safe. That evidence might be tests, types, constraints, metrics, or diagrams. A useful sequence for both interviews and production work is:

text
requirement -> constraints -> model -> implementation -> failure analysis -> verification

Do not jump directly from a requirement to a library call. First state what must remain true, including the behavior expected when something goes wrong. Then choose the mechanism that enforces those conditions.

Deep dive

1. Functional requirements

Start with the actions and data flows that define the system's purpose. In an interview, prioritize two or three critical capabilities instead of trying to design every possible feature. This gives you enough room to make the important contracts and failure behavior precise.

Decision rule: Use functional requirements deliberately when doing so makes the contract or invariant easier to prove. If the approach merely reduces typing while hiding an assumption, prefer the more explicit design.

2. Non-functional requirements

Write down the expectations that shape the architecture: latency, availability, durability, consistency, security, privacy, throughput, geographic reach, and cost. Not every system needs a hard number for every category, but any expectation that materially influences a design choice must be visible. For example, a low-latency read path and a multi-region durability requirement can lead to very different choices.

Decision rule: Use non-functional requirements deliberately when doing so makes the contract or invariant easier to prove. If the approach merely reduces typing while hiding an assumption, prefer the more explicit design.

3. Scope and non-requirements

State what is intentionally excluded. A boundary such as “content creation and feed reading, but not ad auctions” is a strength, not an omission. It tells the listener where you are going to spend design effort and prevents unrelated requirements from silently changing the architecture.

Decision rule: Use scope and non-requirements deliberately when doing so makes the contract or invariant easier to prove. If the approach merely reduces typing while hiding an assumption, prefer the more explicit design.

4. Actors and trust boundaries

List the users, internal services, third parties, operators, and automated clients that interact with the system. The same endpoint may need different authentication, authorization, rate-limit, and data-exposure rules depending on the actor. A client supplied by a user is not a trusted source just because it is your application's client.

Decision rule: Use actors and trust boundaries deliberately when doing so makes the contract or invariant easier to prove. If the approach merely reduces typing while hiding an assumption, prefer the more explicit design.

5. Core entities

Name the important data objects and relationships before you draw APIs or select databases. Entities expose questions about identity, ownership, lifecycle, and consistency. Those questions are often where the real design work is: who owns an object, how long it lives, and what must happen when two operations touch it at the same time.

Decision rule: Use core entities deliberately when doing so makes the contract or invariant easier to prove. If the approach merely reduces typing while hiding an assumption, prefer the more explicit design.

6. Success criteria

Define what “good” means. Use measurable goals when you can, and directional goals when the prompt does not provide enough information. This gives later trade-offs something concrete to defend instead of encouraging claims that a design is simply “scalable.”

Decision rule: Use success criteria deliberately when doing so makes the contract or invariant easier to prove. If the approach merely reduces typing while hiding an assumption, prefer the more explicit design.

Worked example

Consider a large-scale distributed service whose requirements, traffic, failure modes, cost, and operational constraints must all be made explicit. Begin by writing the requirement in one sentence. Then list the input and output contracts, and identify which of the concepts above owns each failure mode.

The key design move is separation of concerns. Parsing and basic validation belong at the boundary. Domain rules belong in the domain or service layer. Persistence rules belong in the database or repository. Presentation rules belong in the client. Mixing these responsibilities can make a happy-path demo look shorter, but it makes edge cases and ownership much harder to reason about.

text
Client
  |
DNS -> CDN / Edge
  |
Load Balancer -> API instances -> Cache
                          |          |
                          +------> Primary datastore
                          |
                          +------> Queue / Stream -> Workers

Walk through at least four cases:

  • the normal path;
  • an empty or missing value;
  • a duplicate, retry, or concurrent path where that behavior is relevant;
  • a dependency failure.

For each case, say which layer detects the problem and what the caller observes. That level of ownership and observable behavior is what a senior code review or technical interview is looking for. It is not enough to say that the system “handles errors”; explain where the error is recognized and how it crosses the boundary.

Production perspective

Production correctness is broader than “the code works on my machine.” Consider what happens during deploys, retries, partial failures, stale clients, concurrent requests, malformed data, schema changes, and high-cardinality workloads. Prefer explicit contracts, bounded resource usage, structured errors, and measurable behavior. Optimize only after evidence identifies the bottleneck or risk.

When an external dependency is involved, define both a timeout and a cancellation strategy. When persistence is involved, define transaction and consistency expectations. When the system exposes user-visible state, account for loading, empty, error, stale, and success states. When security is involved, assume the client can be modified and all network input is untrusted. Client behavior is useful for presentation, but it cannot substitute for server-side authorization, validation, or persistence guarantees.

Guided lab

Take a “design a photo sharing service” prompt. Spend 15 minutes only on prioritized functional requirements, non-functional requirements, assumptions, actors, core entities, and explicit non-requirements.

Complete the lab with this discipline:

  1. Write the requirement and two non-requirements.
  2. List input, output, and error contracts before implementation.
  3. Implement the smallest correct vertical slice.
  4. Add at least one invalid-input test and one edge-case test.
  5. Instrument or inspect the behavior instead of guessing.
  6. Refactor one hidden assumption into an explicit type, constraint, function, or configuration.
  7. Explain one alternative design and why you did not choose it.
  8. Record a short “what would break at 10× scale?” note.

The lab is deliberately narrow. You are practicing requirement discovery and design justification, not attempting to build an entire social platform in 15 minutes.

Edge cases and failure modes

  • Functional requirements: test absence, malformed input, duplicates, ordering or concurrency where applicable, and behavior at the smallest and largest credible sizes.
  • Non-functional requirements: test absence, malformed input, duplicates, ordering or concurrency where applicable, and behavior at the smallest and largest credible sizes.
  • Scope and non-requirements: test absence, malformed input, duplicates, ordering or concurrency where applicable, and behavior at the smallest and largest credible sizes.
  • Actors and trust boundaries: test absence, malformed input, duplicates, ordering or concurrency where applicable, and behavior at the smallest and largest credible sizes.
  • Core entities: test absence, malformed input, duplicates, ordering or concurrency where applicable, and behavior at the smallest and largest credible sizes.

These checks are intentionally repetitive across the categories. The same input or concurrency condition can expose a different defect depending on whether the problem is in the requirement, boundary, entity model, or operational constraint.

Common mistakes and debugging

  • Solving the example instead of the requirement: a copied pattern may be syntactically correct while being architecturally wrong for the actual constraints.
  • Hiding uncertainty with assertions, broad exception handlers, permissive schemas, or “temporary” any values.
  • Testing only the happy path, which means discovering the real contracts only after integration.
  • Optimizing before measuring, or selecting a scalable mechanism without a scale requirement.
  • Letting client-side behavior stand in for server-side authorization, validation, or persistence guarantees.

For debugging, reproduce the smallest failing case first. Inspect the actual value or execution plan, trace the boundary where the invariant first becomes false, and fix the layer that owns the problem rather than adding a downstream patch. This keeps the diagnosis tied to the system's contracts instead of masking the symptom.

Interview questions

  1. What problem do Functional requirements solve, and what trade-off or failure mode would make you choose a different approach?
  2. What problem do Non-functional requirements solve, and what trade-off or failure mode would make you choose a different approach?
  3. What problem do Scope and non-requirements solve, and what trade-off or failure mode would make you choose a different approach?
  4. What problem do Actors and trust boundaries solve, and what trade-off or failure mode would make you choose a different approach?
  5. What problem do Core entities solve, and what trade-off or failure mode would make you choose a different approach?

Checkpoint

Without notes, explain System Design Interview Method: Functional Requirements, Non-Functional Requirements, and Scope to another developer in five minutes. Include one invariant, one edge case, one production failure mode, and one alternative design. Then implement a small example without copying the lesson code.

Mastery checklist

  • I can define the core terms precisely.
  • I can choose a design from requirements instead of from habit.
  • I can implement and test the normal path and edge cases.
  • I can explain the runtime, storage, or complexity cost.
  • I can identify which layer owns validation, errors, and recovery.
  • I can compare at least two reasonable alternatives.
  • I can explain how the design changes at larger scale or stricter reliability.

References

Reader page: /system-design/lesson/248/system-design-interview-method-functional-requirements-non-functional-requirements-and-scope