00:00

Functional vs. Non-Functional Requirements: What Every System Designer Should Know

Every system design starts with requirements. They come in two kinds: functional requirements, which describe what the system should do, and non-functional requirements, which describe how well it should do it. Getting both right is the difference between a system that merely works on a demo day and one that stays fast, secure and reliable for real users.

This tutorial explains both types in simple terms, shows how to write them clearly, compares them side by side, applies them to a real example, and shows how each type appears in Java code.

1. Functional Requirements

Functional requirements describe the specific behaviour of a system: the features, calculations, data handling and interactions it must support. They define what the system should do.

  • A user should be able to log in using an email and password.
  • The system should allow users to place an order online.
  • The application should generate monthly sales reports automatically.
  • An admin should be able to deactivate a user account.
  • The system should send an email when an order is shipped.

Functional requirements are usually written as features or user stories, for example: "As a customer, I want to track my order so that I know when it will arrive." They are either met or not met, which makes them easy to test.

Simple analogy: For a car, the functional requirements are "it must start, steer, accelerate and brake". The non-functional requirements are "it must reach 100 km/h in 10 seconds, use less than 6 litres per 100 km and pass a crash test".

2. Non-Functional Requirements

Non-functional requirements (NFRs) describe the quality attributes of a system. They focus on how the system performs its functions, rather than what the functions are.

  • The home page should load within 2 seconds for 95% of requests.
  • The application should be available 99.9% of the time each month.
  • All personal data should be encrypted in transit and at rest.
  • The system should support 10,000 users online at the same time.
  • The checkout page should meet WCAG 2.1 AA accessibility guidelines.

NFRs are sometimes called quality attributes, system qualities or "-ilities" (scalability, availability, reliability, maintainability). They ensure the system is fast, reliable, secure and usable.

3. Common Types of Non-Functional Requirements

TypeQuestion It AnswersExample Requirement
PerformanceHow fast is it?Search returns results in under 300 ms at p95
ScalabilityCan it grow?Handle 5x today's traffic by adding servers, with no code changes
AvailabilityIs it up when needed?99.95% monthly uptime
ReliabilityDoes it work correctly without failing?Fewer than 0.1% of payments fail because of system errors
SecurityIs it protected?Passwords hashed with bcrypt; all traffic over HTTPS
UsabilityIs it easy to use?A new user can place an order in under 3 minutes
MaintainabilityIs it easy to change?A typical bug fix can be released within one day
ObservabilityCan we see what is happening?Every request has a trace ID in logs and metrics
DurabilityIs data safe from loss?Daily backups kept for 30 days; data loss no more than 5 minutes
ComplianceDoes it follow the rules?Personal data handled according to GDPR
PortabilityCan it run elsewhere?Runs on any cloud that supports Kubernetes
CostIs it affordable?Infrastructure cost below $0.01 per order

Learn more about individual qualities in Availability, Latency, Throughput and Scalability.

4. Key Differences

AspectFunctional RequirementsNon-Functional Requirements
DefinitionWhat the system should doHow well the system should do it
FocusFeatures, actions, business rulesPerformance, security, reliability, usability
ExampleUser login, payment processingResponse time, uptime, scalability
MeasurementYes/No: the feature works or it does notNumbers: seconds, percentages, requests per second
TestingUnit, integration and acceptance testsLoad, stress, security and chaos tests
Defined byBusiness users, product ownersArchitects and engineers, with the business
If missingA feature is absentThe system may be slow, unsafe or keep crashing
Effect on designShapes components, APIs and dataShapes architecture, infrastructure and technology choices

5. How to Write Good Requirements

A useful requirement is clear, specific and testable. Vague requirements cause arguments later, because nobody can prove whether they were met.

Vague (Avoid)Clear and Testable (Prefer)
"The system should be fast.""The product page loads in under 1.5 seconds for 95% of requests at 2,000 requests per second."
"The system should always be available.""The ordering service is available 99.9% of the time each calendar month."
"The system should be secure.""All APIs require a valid OAuth 2.0 token, and failed logins are limited to 5 per minute."
"Users can search.""Users can search products by name, category and price range, sorted by relevance."

A good checklist is SMART: Specific, Measurable, Achievable, Relevant and Testable. For NFRs, always state the metric, the target and the conditions (for example, "at peak load").

6. Example: A Food Delivery App

Functional Requirements

  1. Customers can browse restaurants near their location.
  2. Customers can add dishes to a cart and place an order.
  3. Customers can pay by card or digital wallet.
  4. Restaurants can accept or reject incoming orders.
  5. Delivery partners can see assigned orders and update their status.
  6. Customers can track the delivery on a live map.
  7. Customers can rate the restaurant and the delivery.

Non-Functional Requirements

  • Performance: restaurant search responds in under 500 ms at p95.
  • Scalability: support 50,000 orders per hour at lunch and dinner peaks.
  • Availability: ordering and payment are available 99.95% of the time.
  • Reliability: no order is lost or charged twice, even if a service restarts.
  • Security: card data is handled only by a PCI DSS compliant payment provider.
  • Freshness: the driver's location on the map is no more than 10 seconds old.
  • Usability: the app works on phones with slow 3G connections.

7. How Requirements Shape the Design

Functional requirements tell you which components to build. Non-functional requirements usually decide how to build them, and they drive most of the big architecture choices:

RequirementDesign Decision It Leads To
Search under 500 ms at p95Dedicated search engine and a cache for popular restaurants
50,000 orders per hour at peakStateless services, horizontal scaling and a message queue for spikes
99.95% availabilityMultiple instances across availability zones, health checks and failover
No order charged twiceIdempotency keys on payments and the transactional outbox pattern
Location no older than 10 secondsWebSockets or push updates instead of slow polling
Card data handled securelyHosted payment pages from a certified provider; no card storage

Two systems with the same functional requirements can look completely different if their NFRs differ. A bookstore for 100 users and one for 100 million users have the same features but very different architectures.

8. Requirements in Java Code

A Functional Requirement as Code and Test

Requirement: "An order can be placed only if every item is in stock; the total is the sum of item prices times quantities."

OrderService.java
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

public class OrderService {

    public record OrderLine(String productId, int quantity, BigDecimal unitPrice) {
    }

    private final Map<String, Integer> stock;

    public OrderService(Map<String, Integer> stock) {
        this.stock = stock;
    }

    public BigDecimal placeOrder(List<OrderLine> lines) {
        for (OrderLine line : lines) {
            int available = stock.getOrDefault(line.productId(), 0);
            if (available < line.quantity()) {
                throw new IllegalStateException("Out of stock: " + line.productId());
            }
        }
        return lines.stream()
                .map(line -> line.unitPrice().multiply(BigDecimal.valueOf(line.quantity())))
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
}
OrderServiceTest.java (JUnit 5)
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;

class OrderServiceTest {

    private final OrderService service = new OrderService(Map.of("pizza", 5, "cola", 0));

    @Test
    void calculatesTotalWhenItemsAreInStock() {
        BigDecimal total = service.placeOrder(List.of(
                new OrderService.OrderLine("pizza", 2, new BigDecimal("9.50"))));

        assertEquals(new BigDecimal("19.00"), total); // functional: yes or no
    }

    @Test
    void rejectsOrderWhenItemIsOutOfStock() {
        assertThrows(IllegalStateException.class, () -> service.placeOrder(List.of(
                new OrderService.OrderLine("cola", 1, new BigDecimal("1.50")))));
    }
}

Functional requirements map directly to business logic and are verified by tests that pass or fail.

Checking a Non-Functional Requirement

Requirement: "95% of search requests finish in under 300 ms." NFRs are checked with numbers. This program calculates the 95th percentile (p95) of measured response times and compares it to the target:

LatencyRequirementCheck.java
import java.util.Arrays;

public class LatencyRequirementCheck {

    private static final long TARGET_P95_MILLIS = 300;

    /** Returns the value below which the given percentage of samples fall. */
    static long percentile(long[] samples, double percent) {
        long[] sorted = samples.clone();
        Arrays.sort(sorted);
        int index = (int) Math.ceil(percent / 100.0 * sorted.length) - 1;
        return sorted[Math.max(0, index)];
    }

    public static void main(String[] args) {
        // Response times (ms) of 20 search requests, e.g. collected during a load test
        long[] latencies = {120, 95, 180, 210, 140, 160, 250, 130, 110, 175,
                            190, 205, 145, 170, 230, 260, 280, 150, 610, 640};

        long p50 = percentile(latencies, 50);
        long p95 = percentile(latencies, 95);

        System.out.println("p50 = " + p50 + " ms");
        System.out.println("p95 = " + p95 + " ms (target: under " + TARGET_P95_MILLIS + " ms)");
        System.out.println(p95 < TARGET_P95_MILLIS ? "Requirement MET" : "Requirement NOT met");
    }
}
Output
p50 = 175 ms
p95 = 610 ms (target: under 300 ms)
Requirement NOT met

Only two requests out of 20 were slow (610 ms and 640 ms), but that is 10% of users, so the p95 target is missed. The average here is about 223 ms, which looks fine and would hide the problem. This is why NFRs use percentiles instead of averages.

Non-Functional Requirements in Configuration

Many NFRs are implemented in configuration rather than business logic. In Spring Boot, for example:

application.yml
server:
  compression:
    enabled: true                # performance: smaller responses
  ssl:
    enabled: true                # security: HTTPS only

spring:
  threads:
    virtual:
      enabled: true              # scalability: handle many concurrent I/O requests

resilience4j:
  timelimiter:
    instances:
      payment:
        timeout-duration: 2s     # reliability: never wait forever on the payment provider

management:
  endpoints:
    web:
      exposure:
        include: health, prometheus   # observability: health checks and metrics

9. Gathering and Prioritising Requirements

  • Talk to stakeholders: customers, product owners, support teams and operations all have different needs.
  • Write user stories and use cases for functional requirements.
  • Ask "how much?" and "how fast?" to turn vague wishes into measurable NFRs.
  • Prioritise with MoSCoW: Must have, Should have, Could have, Won't have (this time).
  • Record trade-offs: some NFRs conflict, such as strong security and maximum convenience, so agree on which wins.
  • Revisit regularly: requirements change as the product and its users grow.

10. Using Requirements in System Design Interviews

The first few minutes of any system design interview should be spent on requirements. A good structure:

  1. List 3–5 core functional requirements and agree on what is out of scope.
  2. Ask about scale: number of users, requests per second and data size.
  3. Agree on key NFRs: latency, availability, consistency and durability targets.
  4. Use the NFRs to justify every design choice later, for example "we add a cache because search must be under 300 ms".

See System Design Process and Methodologies for the full process.

11. Common Mistakes

  • Ignoring NFRs until the end: performance and security problems are expensive to fix after launch.
  • Vague targets such as "fast" or "secure" that nobody can test.
  • Using averages instead of percentiles, which hides slow requests.
  • Asking for everything at the maximum, such as five nines of availability for an internal tool.
  • Mixing design decisions into requirements, for example "use Redis" instead of "return results in 100 ms".
  • Not testing NFRs with load, stress and security tests before release.

12. Summary

Functional requirements define what a system does: its features and business rules. Non-functional requirements define how well it does them: performance, scalability, availability, security, usability and more. Functional requirements are tested with pass or fail tests; NFRs are measured with numbers such as percentiles, uptime and throughput. Clear, measurable requirements of both kinds are the foundation of good system design, and NFRs in particular drive most of the architecture. Understanding both is key to building a system that not only works but also performs efficiently, securely and reliably.