Non-Functional Requirements in Software Architecture: How to Define Them
Most engineers I work with can tell you what a system should do. Few can tell you how well it should do it. That gap between "what" and "how well" is wher...
24 Jan 2026

Most engineers I work with can tell you what a system should do. Few can tell you how well it should do it. That gap between "what" and "how well" is where non-functional requirements live, and ignoring them is how you end up with systems that technically work but fail in production.
I've seen this play out at Tipalti, at Mecca Brands, and in every startup I've consulted for. A feature ships. It passes all the functional tests. Then it hits real traffic and everything falls apart. Why? Because nobody asked: "How fast should this be? How many users? What happens when the database is slow? Can we recover from a failure?"
Non-functional requirements answer those questions. They define the quality attributes of your system: performance, reliability, security, scalability, maintainability. They're the constraints that shape your architecture decisions, the metrics that tell you if you're building the right thing, and the guardrails that prevent you from shipping something that works in theory but breaks in practice.
What Are Non-Functional Requirements, Really?
Functional requirements tell you what the system does. "Users can create an account." "The system processes payments." "Reports generate monthly summaries." These are features, behaviors, business logic.
Non-functional requirements tell you how well the system does those things. "Account creation must complete in under 200ms for 95% of requests." "Payment processing must handle 10,000 transactions per second." "Reports must generate within 5 minutes for datasets up to 100GB."
The difference matters because it changes how you build. If your only requirement is "users can create accounts," you might build a simple CRUD API with a single database. If your requirement is "users can create accounts in under 200ms at 10k QPS," you need caching, connection pooling, maybe read replicas, and a completely different architecture.
Non-functional requirements are constraints. They force trade-offs. They make you choose between consistency and availability, between speed and cost, between simplicity and resilience. They're what separate a prototype from a production system.
The Categories That Actually Matter
I organize non-functional requirements into categories that map to real engineering decisions. Not academic theory. Practical buckets that help me make architecture choices.
Performance and Scalability
This is where most teams start, and where most teams get it wrong. They say "it needs to be fast" or "it should scale." That's useless. Fast compared to what? Scale to how many users?
I break performance down into measurable dimensions:
Latency: How long does an operation take? I specify percentiles, not averages. P50, P95, P99. Average latency hides outliers. If your P99 is 5 seconds but your average is 200ms, you have a problem that averages won't reveal.
Throughput: How many operations per second? Reads and writes separately. A system that handles 100k reads per second but only 100 writes per second needs different architecture than one with balanced traffic.
Capacity: How much data? How many concurrent users? Storage size, memory usage, network bandwidth. These numbers determine whether you need sharding, whether you can fit everything in memory, whether you need a CDN.
Scalability: How does performance change as load increases? Linear? Sub-linear? Does it degrade gracefully or fall off a cliff?
I write these as specific, measurable targets. "API endpoints must respond in under 100ms for P95 latency at 5,000 requests per second." "The system must support 10 million active users with 1TB of data storage."
Reliability and Availability
Reliability is about correctness. Availability is about uptime. They're related but different.
Availability: What percentage of time is the system operational? 99.9% means 8.76 hours of downtime per year. 99.99% means 52.56 minutes. These numbers drive decisions about redundancy, failover, disaster recovery.
Fault Tolerance: What happens when components fail? Can the system degrade gracefully? Do we need active-active replication or is active-passive enough?
Recovery Time: How quickly can we recover from a failure? Recovery Time Objective (RTO) and Recovery Point Objective (RPO) matter. If we can lose 1 hour of data and be back online in 15 minutes, that's very different from needing zero data loss and 30-second recovery.
Error Rates: What's acceptable? 0.1% error rate? 0.01%? This affects how much error handling, retry logic, and circuit breakers you need.
I've seen teams aim for 99.99% availability without understanding what that costs. Multiple data centers, automated failover, constant monitoring, and a budget for redundancy. Sometimes 99.9% is enough.
Security
Security requirements constrain how you implement features, not what features you build. They're also the requirements that get ignored until there's a breach.
Authentication and Authorization: Who can access what? How do we verify identity? Do we need multi-factor authentication?
Data Protection: Encryption at rest and in transit. Compliance requirements (GDPR, PCI-DSS, HIPAA) drive specific technical choices.
Vulnerability Management: How quickly do we patch known vulnerabilities?
Audit and Compliance: What do we log? How long do we retain logs? What compliance frameworks apply?
I write security requirements as specific technical constraints. "All API communication must use TLS 1.3." "Passwords must be hashed using bcrypt with cost factor 12." "Personal data must be encrypted at rest using AES-256."
Maintainability and Operability
This is where junior engineers and most product managers stop thinking. They assume the system will just work. It won't.
Observability: What metrics do we need? What logs? What traces? Can we debug production issues? I've spent too many nights debugging production problems with insufficient observability. Now I define this upfront.
Deployment: How do we deploy? Zero-downtime? Blue-green? Canary? What's the rollback process?
Monitoring and Alerting: What do we alert on? How do we know when something is wrong?
Testability: How do we test this? Unit tests? Integration tests? Can we test failure scenarios?
I write these as operational constraints. "All services must expose Prometheus metrics for latency, error rate, and throughput." "Deployments must complete in under 5 minutes with zero-downtime."
Usability and Accessibility
For user-facing systems, these matter. They constrain implementation, not functionality.
Response Time: How quickly does the UI respond? Perceived performance matters more than actual performance sometimes.
Accessibility: WCAG compliance? Screen reader support? Keyboard navigation?
Browser/Device Support: Which browsers? Which versions? This affects JavaScript features, CSS, and polyfills.
I specify these as concrete constraints. "Page load time must be under 2 seconds on 3G networks." "The application must be WCAG 2.1 AA compliant."
How to Define Non-Functional Requirements: A Practical Process
Step 1: Ask the Right Questions
Start with questions, not assumptions.
Performance: What's the expected traffic? Peak? Average? How many concurrent users? What's the acceptable response time? How much data? What's the read/write ratio?
Reliability: What's the cost of downtime per minute? How quickly do we need to recover? How much data can we lose? What user journeys must always work?
Security: What data are we handling — personal, financial, health? What compliance requirements apply? Who are the users — internal, external, both?
Operational: Who's operating this? What's the deployment frequency? What tools do we already have?
I ask these questions in meetings with product, business, and operations. I don't assume I know the answers.
Step 2: Quantify Everything
Vague requirements are useless. "Fast" means nothing. "Scalable" means nothing.
I turn every requirement into a number with a unit:
- "Fast" becomes "P95 latency under 200ms"
- "Scalable" becomes "handles 10k QPS with linear scaling to 50 instances"
- "Secure" becomes "TLS 1.3, AES-256 encryption at rest, OWASP Top 10 compliance"
If I can't quantify it, I push back. A wrong number is better than no number because it gives you something to test against.
Step 3: Prioritize and Trade Off
Not all requirements are equal. Some conflict.
Must Have (P0): System doesn't work without these. "Payment processing must be PCI-DSS compliant."
Should Have (P1): Important but not critical. "API should respond in under 100ms for 95% of requests."
Nice to Have (P2): Improve experience but not essential. "Dashboard should load in under 1 second."
Document conflicts explicitly. "We prioritize availability over consistency for the read path." "We accept higher latency for stronger security in authentication flows."
Step 4: Make Them Testable
A requirement you can't test is a requirement you can't verify.
Instead of: "The system should be fast"
I write: "The /api/users endpoint must respond in under 100ms for 95% of requests, measured over a 24-hour period with production traffic patterns."
That's testable. I can write a load test. I can set up monitoring. I can verify it in production.
Step 5: Document and Communicate
Requirements that live only in my head are useless. I document them. I share them. I make them part of the architecture decision record.
I use a simple format:
Requirement: [Clear statement]
Category: [Performance | Reliability | Security | etc.]
Priority: [P0 | P1 | P2]
Measurement: [How we measure it]
Target: [Specific number or criteria]
Current State: [Where we are now]
Trade-offs: [What we're giving up]
When someone proposes a change that violates a requirement, I point to the document. "This violates our P95 latency requirement of 100ms. How do we address this?"
Real Examples from Production Systems
Payment Processing API (Tipalti)
Performance: P95 latency under 500ms. 5,000 TPS. 100 million transactions per month.
Reliability: 99.99% availability. 5-minute RTO. Zero data loss (RPO: 0). Error rate under 0.01%.
Security: PCI-DSS Level 1. TLS 1.3. AES-256 at rest. Tokenized card numbers. Full audit logging.
Trade-offs: Chose strong consistency over performance (money is involved). Chose higher cost (redundant infra) over lower availability. Chose slower development velocity (compliance overhead) over faster feature delivery.
These requirements drove architecture decisions: PostgreSQL for ACID guarantees, Redis for caching with careful invalidation, multiple data centers, comprehensive monitoring.
Content Delivery System
Performance: P95 under 200ms for metadata API, under 2 seconds for file downloads. 50k reads/s, 1k writes/s. 10TB storage growing at 100GB/month.
Reliability: 99.9% availability. 30-minute RTO. 1-hour RPO.
Trade-offs: Chose eventual consistency (acceptable for content). Chose lower availability target (99.9% vs 99.99%) to reduce costs. Chose CDN caching over always-fresh content.
Decisions: S3 for storage, CloudFront for CDN, eventual consistency for metadata, aggressive caching.
Common Mistakes I've Made
Assuming instead of asking. I used to assume "fast" meant what I thought it meant. Fix: always get numbers.
Requirements without business context. Why does it need to be this fast? What's the business impact of being slower? Connect requirements to business outcomes.
Unmeasurable requirements. "The system should feel responsive." How do you measure "feel"? Every requirement must be measurable.
Ignoring operational requirements. No monitoring. No deployment process. No runbooks. Include operational requirements from day one.
Conflicting requirements without acknowledgment. High availability and low cost conflict. Document the trade-offs explicitly.
Requirements that never get updated. The system evolves, but requirements stay frozen. Review and update regularly.
How to Validate
Load testing. Simulate production traffic with k6 or JMeter. Push the system until it breaks, then you know the limits.
Monitoring. Prometheus for metrics. Grafana for dashboards. Set up alerts based on requirements.
Chaos engineering. Kill a database. Slow the network. Fill up disk space. Does the system meet RTO and RPO targets?
Code reviews. Use requirements as criteria. "Does this change violate our latency requirements?"
The Bottom Line
Non-functional requirements aren't optional. They're the difference between software that works in a demo and software that works in production.
The process isn't complicated. Ask questions. Quantify everything. Prioritize. Make them testable. Document. But it requires discipline. It requires pushing back when requirements are vague. It requires making hard choices about trade-offs.
Start now. Take 30 minutes. Write down the non-functional requirements for your current system. Make them specific. Make them measurable. Share them with your team. I guarantee you'll find gaps, assumptions, and conflicts you didn't know existed.