Õppetunnile juurdepääsu kontrollimine...
Functional vs Non-Functional Requirements
What the system does versus how well it must do it, without inventing targets.
Vaatad tasuta eelvaate õppetundi.
In the previous lesson, we analysed the initial product requirement and separated explicitly stated behaviour from assumptions, open questions, risks and scope uncertainty. The next step is to structure those requirements more clearly.
When designing a backend system, requirements can broadly be divided into two categories: functional requirements and non-functional requirements. This distinction matters because understanding which features a system supports is not enough. We also need to understand:
- how reliable it needs to be
- how secure it needs to be
- how it should perform under expected load
- how it should behave when failures occur
- how easy it should be to operate and observe
- how safely it can evolve over time
In this lesson, we will examine both categories in the context of our Order Management Backend.
What Is a Functional Requirement?
A functional requirement describes what the system does. A useful way to think about it is:
Which user or system can perform which action, and what behaviour should that action produce?
"Customer can view available products" is a functional requirement because it describes a capability. So are "Customer can create an order containing multiple products" and "Administrator can update product inventory". Functional requirements define observable system behaviour.
Our Current Functional Requirements
From the initial product brief, we can already identify several functional capabilities. In the previous lesson we saw that several of them use words that still need clarification (available, eligible). That gives us an important lesson:
A requirement can be functional without being precise enough to implement.
Looking at the capabilities one at a time adds a few observations:
- Order creation is connected to Inventory: Customers can only order Products available in Inventory. So Order creation is not merely an
INSERT INTO orders; it is a business workflow involving several rules. - Price calculation is functional behaviour too. The backend must not trust a client-provided total; the server determines the authoritative prices. At requirement level: the backend owns the Order price calculation.
- Order history raises refinements: can Customers view only their own Orders, how are they sorted, do we need pagination, which details are returned?
- Product and Inventory management says "manage", which is broad. For Products it could mean creating, updating, deactivating, viewing or deleting; for Inventory, setting, increasing, decreasing or viewing the quantity. We cannot assume all of these are required merely because the word
managewas used. - Payment involves an external dependency. Provider behaviour, timeouts, failures and idempotency belong to the Payment integration design later.
A Functional Requirement Is Not an Endpoint
Backend developers often see a requirement and immediately think in HTTP endpoints. "Customer can create an order" may immediately suggest POST /orders. That is natural. But the endpoint is not the requirement. The requirement is that a Customer can create an Order; the API is one mechanism through which we expose that behaviour. Similarly, "Customer can cancel an eligible Order" could be represented as POST /orders/{id}/cancel or some other HTTP design. The requirement itself is independent of that choice.
The flow should go from requirement to technical design to API, not from an endpoint to behaviour invented around it.
Behaviour Should Be Observable
A useful functional requirement should describe behaviour that can later be verified. "The ordering experience should be good" is weak: what does good mean? It is difficult to verify. Compare these, each more precise than the last:
- A Customer can create an Order containing one or more valid Products.
- If sufficient Inventory is available, the system creates the Order and returns its identifier.
- If requested Inventory is unavailable, the system rejects the Order.
Now we have behaviour that can be tested.
Functional requirements should also not describe only the happy path. For "Customer can create an Order", what about an unknown Product, an invalid quantity, insufficient Inventory or an empty Order? For payment, what if the provider declines, times out or succeeds while our database update fails? Production quality is often revealed more clearly by failure behaviour than by the happy path. This is why functional requirements naturally lead into acceptance criteria, which we will cover next.
What Is a Non-Functional Requirement?
A non-functional requirement describes how the system should operate, or which quality and operational constraints matter around its functionality. For the functional requirement "Customer can create an Order", non-functional concerns might be:
- Order creation must remain reliable under expected production traffic.
- A Customer must never access another Customer's private Order data.
- The service must expose enough operational information for engineers to diagnose failures.
These do not introduce new business features. They define qualities and constraints around existing features. Take one feature, "Customer can view Order history", which is functional, and look at the expectations around it:
| Requirement | Kind of concern |
|---|---|
| Order history must only expose Orders belonging to the authenticated Customer. | Security |
| Order history should remain responsive under expected production traffic. | Performance |
| Failures while loading Order history should be diagnosable through logs and metrics. | Observability |
The feature remains the same. The expectations around it change.
Backend systems commonly need to consider performance, scalability, reliability, availability, security, data integrity, maintainability, observability, recoverability and compliance. Not every project needs all of these at the same depth, but a backend engineer should recognize them. The next sections look at each.
Performance and Scalability
Performance concerns how quickly and efficiently a system performs its work: API response latency, database query latency, requests handled per second or batch processing duration. Our brief provides no concrete latency target, so instead of inventing "All endpoints must respond within 200 ms" we ask whether there are explicit response-time expectations and what traffic the first version should support.
Scalability concerns how a system behaves as load grows. A system processing 10 Orders per minute does not necessarily need the same architecture as one processing 10,000 Orders per second:
Do not build for massive scale before understanding the expected scale.
Reliability and Availability
Reliability asks whether the system behaves correctly and consistently even when things go wrong. For an Order system, this matters greatly:
- Payment succeeded, but the Order remained
UNPAID: a reliability problem. - Inventory was reduced, but Order creation failed: a serious consistency problem.
- The same payment was processed twice: also a reliability problem.
Later modules will connect these concerns to transactions, idempotency, external integration design and failure handling.
Availability asks whether the service is available to serve user requests. A backend can have excellent features and still be unusable if it is frequently unavailable. Questions include: Is this service business-critical? Is occasional downtime acceptable? Do we have an availability target? Our current product requirement does not specify an SLA or availability target, so we do not invent one.
Security
Security is central to backend requirements, and it usually appears as an expectation attached to a functional requirement:
| Functional requirement | Security expectation |
|---|---|
| Customer can view previous Orders. | Customer must not be able to view another Customer's Orders. |
| Administrator can update Inventory. | Regular Customers must not have access to administrative Inventory operations. |
These requirements will later drive our authentication and authorization design.
Data Integrity
Data integrity is particularly important for our Order Management Backend. Examples:
- Inventory must not become negative because of successful Order placement.
- Order totals must remain internally consistent with recorded OrderItems.
- Historical Orders must not accidentally change because current Product data changes.
Is "Inventory must never become negative" functional or non-functional? One engineer may call it a business invariant, another a data-integrity requirement, another functional correctness. The categorization matters less than the outcome: the rule is explicit, everyone understands it, the implementation protects it and tests verify it.
Maintainability
Production software must remain changeable. Engineers will need to work on this system next month and next year. Maintainability questions include:
- Can engineers understand the code?
- Are responsibilities separated clearly?
- Can important behaviour be tested?
- Can configuration change safely?
- Can new functionality be added without rewriting unrelated parts of the system?
These expectations are not always written explicitly in a Product requirement. But they still matter to engineering design.
Observability
When production software fails, engineers need to understand what happened. Observability may include logs, metrics, health checks, request correlation, operational dashboards and tracing.
Within this course, we will later work with structured logging, metrics, health checks and request/correlation IDs. Advanced distributed tracing architecture is outside the scope of this course; we do not need it for our current modular application.
Recoverability
We also need to think about what happens after failures:
- A deployment failed: can we roll back?
- A database migration failed: can the previous application version continue working?
- The external payment provider timed out: can the operation safely retry?
These concerns become important later in the deployment, database migration and external service integration modules.
Compliance Is Context-Dependent
Some systems have legal or regulatory requirements, such as financial regulation, personal-data requirements or audit requirements. Our fictional product brief does not specify any particular compliance obligation, so we will not invent one for the course project. On a real project, however, an engineer should ask:
Are there compliance requirements that affect this feature?
Non-Functional Requirements Should Be Measurable
"The system should be fast" is weak, because fast is not measurable. "The system should meet the agreed latency target for normal production traffic" is better, and once a real target is known it might become "95% of requests should complete within the agreed number of milliseconds". The same applies to availability. But the number must come from actual engineering or business context.
A specification can also look professional while still being meaningless. Targets such as 99.999% availability, a 50 ms response time or 100,000 requests per second may sound impressive, but without context they are arbitrary. Every target has a cost: higher availability needs more redundancy, which brings more operational complexity and more cost. Requirements should reflect real needs, not impressive-looking numbers.
Functional Requirement Decomposition
A broad requirement such as "Customer can place an Order" can be decomposed into smaller behaviours:
- Customer provides one or more requested items.
- System validates the requested Products.
- System validates quantities.
- System checks required Inventory.
- System determines authoritative item prices.
- System calculates the total.
- System persists the resulting Order.
However, be careful. Some decomposition may start moving toward technical design. We should remain aware of the boundary between required behaviour and implementation strategy.
Decomposition also separates ideas that are related but different. "Customer can cancel an Order" is a functional capability. "Only eligible Orders can be cancelled" is a business rule. "A cancelled Order cannot return to an active state" is an invariant. A backend design needs all of them.
Example Requirement Set
After clarification, a feature definition might eventually look like this:
## Feature: Create an Order
### Functional Behaviour
- Customer submits one or more OrderItems.
- Each OrderItem references an existing Product.
- Each quantity must be valid.
- Requested Inventory must be available.
- Backend determines authoritative item prices.
- A valid request creates an Order.
### Non-Functional Concerns
- Security: only an authenticated Customer can create an Order for themselves.
- Reliability: Order creation must not leave partial state if the operation fails.
- Data Integrity: Inventory-related rules must remain valid when concurrent requests occur.
- Observability: unexpected Order creation failures must be diagnosable.
Notice that we still have not written implementation code.
Requirement Traceability
On larger projects, it is valuable to understand why a piece of code exists. Ideally, we can trace a product requirement to an engineering requirement, then to a ticket, the implementation and a test.
For example, the requirement "Customers cannot successfully order more Inventory than is available" later becomes the ticket "Validate and safely consume Inventory during Order creation". Then the implementation protects that rule, and integration tests verify that insufficient Inventory is rejected. This makes it easier to connect code back to business intent.
Testing Starts at Requirement Time
Testing should not begin only after implementation is finished. While reading a requirement, test scenarios should already start appearing. For "Customers should only be able to order Products available in Inventory":
| Inventory | Requested | Expected |
|---|---|---|
| 10 | 2 | success |
| 2 | 2 | should this succeed? |
| 1 | 2 | reject |
| 0 | 1 | reject |
The second row helps clarify whether consuming the final available units is allowed. Thinking about tests can expose unclear requirements before implementation starts.
Our Working Requirement Lists
At this point, we have high-level requirements. We should not treat them as a final specification yet: we have moved from the product brief through requirement analysis to this classification, and clarification, acceptance criteria and technical design are still ahead.
Based on the current product brief, our working functional requirements are:
- FR-1: Customers can browse available Products.
- FR-2: Customers can place Orders.
- FR-3: An Order can contain multiple Products.
- FR-4: Ordering is constrained by available Inventory.
- FR-5: The backend calculates Order totals.
- FR-6: Customers can view previous Orders.
- FR-7: Customers can cancel eligible Orders.
- FR-8: Customers can pay for Orders through the Payment provider.
- FR-9: Administrators can manage Products.
- FR-10: Administrators can manage Inventory.
These are not final. Clarification may change their wording.
Our brief does not specify concrete non-functional targets, but we can still derive the areas that matter from what it describes. These are not SLAs or targets; they are areas where additional requirements or engineering decisions may be necessary:
| NFR area | Why it matters here | Working concern and open questions |
|---|---|---|
| Security | The system distinguishes Customers and Administrators. | Both capabilities must be protected appropriately. How will Customers and Administrators authenticate, and how will authorization be enforced? This will lead us into Spring Security. |
| Data integrity | The backend calculates prices and enforces Inventory availability. | Order, price and Inventory state must remain internally consistent: the backend calculates the total, and successful Orders respect available Inventory (exact behaviour still to be clarified). |
| Reliability | Order creation changes Inventory; Payment calls an external provider. | Failures must not leave unintended partial states. Likely risks: partial Order creation, incorrect Inventory updates, duplicate payment processing, payment timeouts. |
| Performance | Customers browse Products and view Order history. | Expected traffic and list sizes must be understood before targets are set. How many Products and historical Orders? Do list APIs need pagination? |
| Observability | The application will run in production. | Production failures must be diagnosable. What health information should the service expose, and which failures should appear in metrics? We implement this in the production-readiness module. |
| Maintainability | Requirements will keep evolving. | The application should remain understandable and safely changeable. |
Notice that we have not invented an SLA, a latency target or a throughput target, because we do not yet have evidence for them. We are identifying concerns, not inventing numbers.
What Belongs in the Product Requirement?
Not every technical concern belongs in a Product Manager's requirement document. Database indexes, connection pool size and Spring Boot configuration are engineering implementation details. On the other hand, "Customer must not see another Customer's Orders" is important both as product behaviour and as a security expectation. Good collaboration requires understanding the boundary between business requirements and engineering implementation.
Requirement Quality Checklist
When reviewing a requirement, ask whether it is:
- Clear: would different people understand the same behaviour?
- Testable: can we verify whether the behaviour is correct?
- Necessary: does it solve an actual problem?
- Scoped: does it belong in the current release?
- Consistent: does it conflict with another requirement?
- Feasible: can it be implemented within known constraints?
Not every requirement will be perfect. These questions help us improve them.
Technology Is Not a Requirement
A dangerous requirement might say "The system must use microservices" or "The system must use Kafka" without explaining why. These are not functional requirements. They are technical constraints or implementation choices, and technology choices need reasons. A strong engineer asks what behaviour is required, what risks exist, and what the simplest design is that safely satisfies those requirements; module 2 applies that question to our architecture.
Practice Exercise 1: Specify Order History
Solutions: lesson 1.09.
Create docs/feature-order-history.md in your workspace and write FR-6, "Customers can view previous Orders", in the shape of the "Example Requirement Set" above:
- Functional Behaviour: at least four statements, including at least one that is not the happy path.
- Non-Functional Concerns: one line each for security, performance and observability.
- One measurable NFR: rewrite your performance concern so that it could be checked, but leave the numbers as placeholders such as
[agreed target], and write next to each placeholder the question that would produce it and who should answer. - Test thinking: three rows of a table like the one in "Testing Starts at Requirement Time", at least one of which exposes a question.
Practice Exercise 2: Sort the Requirements
Classify each statement as functional, non-functional (name the area), or a technology choice that is not a requirement. Mark the ones that sit between categories and say why.
- An Administrator can deactivate a Product.
- A Customer must not see another Customer's Orders.
- The system must use Kafka.
- Engineers can find a failed request in the logs.
- A failed deployment can be rolled back to the previous version.
- Order history shows the newest Order first.
- A payment that times out must not mark the Order
PAID. - The system should be fast.
Practice Exercise 3: Find the Problem in These NFRs
A teammate adds this to the requirements document:
- NFR-1: Every endpoint responds within 50 ms.
- NFR-2: The service is available 99.999% of the time.
- NFR-3: The service supports 100,000 Orders per second.
Explain in a few sentences what is wrong with them, given what the brief tells us, and write what the document should say instead.
Knowledge Check
Question 1
Why is "Customer can create an Order" not the same requirement as POST /orders?
Question 2
"Customers can browse available Products" is clearly functional. Why is it still not ready to implement?
Question 3
Why do we write "meet the agreed latency target" instead of choosing 200 ms now, even though 200 ms is measurable?
Question 4
In the test-thinking table, why is the row "Inventory 2, requested 2" more useful than "Inventory 10, requested 2"?
Question 5
Two engineers argue whether "Inventory must never become negative" is functional or a data-integrity requirement. Why does the lesson say the argument matters less than something else?
Knowledge Check Answers
Answer 1
The requirement is the capability; the endpoint is one way to expose it. If we start from the endpoint, we invent behaviour around a URL instead of designing the API from the behaviour.
Answer 2
"Available" has several meanings (exists, active, in stock, or both), and they produce different results. A requirement can name a capability and still be too imprecise for an engineer to know what to build.
Answer 3
A number is only meaningful if it comes from real context: expected traffic and what users need. An invented target may cost redundancy and complexity nobody needs, or be too loose, and it looks authoritative while resting on nothing.
Answer 4
It sits on the boundary, and the brief does not say whether ordering the last units is allowed. The obvious row confirms nothing new; the boundary row exposes a question that must be answered before implementation.
Answer 5
What matters is that the rule is explicit, everyone understands it, the implementation protects it and tests verify it. A perfect label on an unprotected rule protects nothing.
Engineering Principle
A feature is not fully understood until both its behaviour and the qualities that matter around that behaviour are considered.
Functional requirements describe what the system does. Non-functional requirements describe qualities and constraints such as security, reliability, performance, observability and maintainability. Production software needs both.
Summary
- Functional requirements describe system capabilities and observable behaviour, including failure behaviour, not just the success path; an API endpoint is one representation of a requirement, not the requirement itself.
- Non-functional requirements describe qualities, constraints and operational expectations: performance, scalability, reliability, availability, security, data integrity, maintainability, observability, recoverability and compliance.
- Non-functional targets should not be invented without evidence; arbitrary latency, throughput and availability numbers do not make a specification professional.
- Some rules sit between categories; a clear, protected and tested rule matters more than perfect categorization.
- Test scenarios can be identified while requirements are still being analysed, and traceability connects product intent to tickets, implementation and tests.
- Technology choices should not masquerade as business requirements: the simplest design that safely satisfies actual requirements is usually the best starting point.
Our requirements are still not ready for implementation. "Customer can create an Order" is still broad: when can an engineering ticket be considered complete? In the next lesson, User Stories and Acceptance Criteria, we will begin turning broad requirements into smaller, testable, engineering-ticket-ready behaviours that define exactly what success looks like.
Mõtle enne
TulekulKontrollküsimused, mille puhul valid vastuse enne, kui õppetund selle näitab.