Non-Functional Requirements (Quality Attributes)
A Non-Functional Requirement (NFR) describes how well a solution must behave — how fast, how available, how secure — as opposed to what it must do.
At a glance
Section titled “At a glance”- What it is: Explicit, measurable targets for quality attributes like performance, availability, security, and scalability, agreed before design is finalized.
- Why it matters: a system that does the right thing but falls over under real load, or fails a security review, has still failed — NFRs are what stop that from being a surprise.
- For developers: NFRs are the numbers behind “why does this need a cache” or “why can’t we just query the primary database directly” — if a design decision seems arbitrary, check whether an NFR is driving it.
- When to use it: alongside Solution Options Analysis, since the right option often depends on the quality bar the solution must hit.
- When not to use it: as a copy-pasted checklist applied without reference to the actual problem — an internal reporting tool doesn’t need the same availability target as a payment system.
- Prerequisites: a scoped problem statement.
Mental model
Section titled “Mental model”graph TD
NFR["Non-Functional Requirements"]
NFR --> Perf["Performance (latency, throughput)"]
NFR --> Avail["Availability (uptime, recovery time)"]
NFR --> Sec["Security (auth, data protection)"]
NFR --> Scale["Scalability (peak load, growth)"]
NFRs group into a handful of recurring categories. Each one needs a specific, measurable target for a given solution — not a generic “should be fast and secure.”
Step-by-step walkthrough
Section titled “Step-by-step walkthrough”- For each NFR category (performance, availability, security, scalability, and any others relevant to the problem), ask what “good enough” actually means for this specific solution.
- Turn each answer into a measurable target — a number or a testable condition, not an adjective.
- Base targets on real data where possible (current peak traffic, existing SLAs, compliance requirements) rather than guessing.
- Record the targets alongside the chosen option’s ADR so they’re checked during design and testing, not just at launch.
- Revisit targets if the scope or expected load changes.
Worked example
Section titled “Worked example”Continuing the retail checkout scenario, scoped around flash-sale traffic.
- Input: a vague instruction to make checkout “reliable and fast.”
- Process: pull actual flash-sale traffic data (5x normal peak) and the business’s tolerance for downtime during a sale.
- Output: measurable NFRs — 99.95% availability during sale windows, p95 latency under 300ms for payment confirmation at 5x normal load, and PCI-DSS-aligned handling of card data.
- Enforcing the target in code: a latency NFR is only real if something in the call path actually bounds it. A Polly timeout policy around the payment gateway call turns “p95 under 300ms” into an enforced budget rather than a hope:
var timeoutPolicy = Policy.TimeoutAsync(TimeSpan.FromMilliseconds(300));
var response = await timeoutPolicy.ExecuteAsync( ct => paymentClient.ConfirmPaymentAsync(request, ct), cancellationToken);Validate the target itself with Azure Load Testing run at 5x normal peak, and monitor the live p95 in Application Insights so a regression is caught before customers notice.
-
Enforcing an availability NFR across regions: a 99.95% availability target usually can’t be met from a single Azure region alone — a regional outage breaches it regardless of how well the app itself is built. Two mechanisms make the target enforceable rather than aspirational:
Scale-out under load — an Azure App Service autoscale rule adds instances as CPU rises, keeping the p95 latency target stable during the flash sale itself:
resource autoscale 'Microsoft.Insights/autoscalesettings@2022-10-01' = {name: 'checkout-autoscale'location: locationproperties: {targetResourceUri: appServicePlan.idenabled: trueprofiles: [{name: 'CPU-based scale-out'capacity: { minimum: '2', maximum: '10', default: '2' }rules: [{metricTrigger: {metricName: 'CpuPercentage'metricResourceUri: appServicePlan.idtimeGrain: 'PT1M'statistic: 'Average'timeWindow: 'PT5M'timeAggregation: 'Average'operator: 'GreaterThan'threshold: 70}scaleAction: { direction: 'Increase', type: 'ChangeCount', value: '1', cooldown: 'PT5M' }}]}]}}Surviving a regional outage — an Azure SQL Database auto-failover group spans two Azure paired regions (e.g. East US/West US), so a full region loss fails over automatically to a secondary replica without breaching the availability target:
resource failoverGroup 'Microsoft.Sql/servers/failoverGroups@2022-05-01-preview' = {parent: primarySqlServername: 'checkout-failover-group'properties: {readWriteEndpoint: { failoverPolicy: 'Automatic', failoverWithDataLossGracePeriodMinutes: 60 }partnerServers: [ { id: secondarySqlServer.id } ]databases: [ checkoutDatabase.id ]}}If the payment confirmation data instead lived in Azure Cosmos DB, the equivalent mechanism is enabling multi-region writes on the account rather than a failover group — useful when the NFR calls for near-zero RPO across regions rather than an automatic-but-brief failover window.
Common use cases
Section titled “Common use cases”| Use case | When to use | Notes |
|---|---|---|
| Comparing solution options | Options score differently against a quality bar | Feed NFRs into Solution Options Analysis rather than deciding on cost alone |
| Load testing before launch | Validating the design actually meets its targets | Test against the specific NFR numbers, not a vague “make sure it can handle load” |
| Vendor/SaaS evaluation | Choosing a third-party component | Check the vendor’s published SLAs against your own NFRs before assuming they match |
Advanced details
Section titled “Advanced details”- Make targets testable: “fast” is not an NFR; “p95 latency under 300ms at 5x peak load” is.
- Tie targets to actual risk tolerance: a batch reporting job and a payment system should not share the same availability target — calibrate to business impact.
- NFRs can conflict: maximizing availability and minimizing cost often pull in different directions; document the trade-off rather than silently picking one.
- Use ISO/IEC 25010 as the source taxonomy, not an ad hoc list: the four categories above are a practical subset of the international standard’s eight software quality characteristics — performance efficiency, reliability, security, compatibility, usability, maintainability, portability, and functional suitability. Checking against the full standard catches categories a quick brainstorm tends to miss, like maintainability or compatibility.
- Write NFRs as quality scenarios, not adjectives: arc42’s quality-scenario technique states each target as stimulus → response — e.g. “when traffic reaches 5x normal peak (stimulus), p95 payment confirmation latency stays under 300ms (response)” — which is inherently testable, unlike a standalone number with no triggering condition.
- Replication is the general mechanism behind autoscale and failover: Martin Fowler’s Patterns of Distributed Systems catalog documents this as the Leader and Followers pattern — one replica accepts writes and propagates them to followers, which is what both SQL failover groups and Cosmos DB replication implement under the hood. Recognizing the general pattern makes it easier to reason about the failover window and consistency trade-offs regardless of which Azure service implements it.
Quick reference
Section titled “Quick reference”| Term | Meaning |
|---|---|
| NFR (Non-Functional Requirement) | A measurable quality target (performance, availability, security, scalability) rather than a feature |
| p95 latency | The response time under which 95% of requests complete — a common performance target |
| SLA (Service Level Agreement) | A committed, measurable target for availability or performance, often with consequences for missing it |
| ISO/IEC 25010 | The international standard defining the eight software quality characteristics NFRs are typically drawn from |
| Quality scenario | An NFR written as stimulus → response (e.g. “at 5x peak load, p95 latency stays under 300ms”), arc42’s technique for making a quality target testable |
| Autoscale rule | A rule that adds/removes compute instances based on a live metric like CPU, keeping a performance NFR stable under load |
| Failover group | An Azure SQL Database construct that automatically fails over to a paired-region replica if the primary region becomes unavailable |
| Paired region | Two Azure regions (e.g. East US/West US) linked for sequenced platform updates and disaster recovery |
Troubleshooting
Section titled “Troubleshooting”| Symptom | Likely cause | Fix |
|---|---|---|
| Design decisions seem arbitrary | NFRs were never made explicit or measurable | Write down specific, testable NFR targets and reference them in design reviews |
| System passes functional tests but fails in production | NFRs weren’t tested, only features were | Add load, security, and failover testing against the stated NFR targets |
| Every requirement says “must be fast and secure” | NFRs copied from a generic checklist | Recalibrate targets to this specific solution’s actual risk and traffic profile |
Check your understanding
Section titled “Check your understanding”- Why is “the system should be reliable” not yet a usable NFR?
- Take a system you work on and write one measurable NFR for performance and one for availability.
Related pages
Section titled “Related pages”Part of Introduction to Solution Architecture. Works alongside Solution Options Analysis & Trade-offs and feeds into Component & Integration Design. See also the Glossary. For the fuller operational treatment of autoscaling and multi-region failover, see Capacity & Scalability Planning and Business Continuity & Disaster Recovery.