Loading...

What Breaks when a Million People use your AI App

The features decide whether your AI app works once. The Non-functional requirements (NFRs) decide whether it keeps working for everyone, under load, while the world misbehaves. Here is the full map, told through the things that actually break.

A demo proves one good answer. Production has to serve good answers to millions of people, over and over, while traffic spikes, models drift, and costs pile up. This is the map of Non-functional requirements (NFRs) for AI apps at scale, the twelve things that decide whether your app survives, explained through what goes wrong when each one is missing.

The demo works. That's exactly the problem.

You built an AI feature. It answers questions, or writes code, or processes a claim. You showed it to your team and it worked. Everyone was impressed. Then you shipped it, a few thousand people showed up, and it started doing things it never did on your laptop. Slow responses. A bill that doubled overnight. One user seeing another user's data. Answers that were confidently wrong.

None of that's a bug in a feature. The feature works. What broke are the requirements nobody wrote down, because they don't show up with one user on a good day. They show up with a million users on a bad one.

Functional vs non-functional, in one line

A functional requirement is what the app does, like summarise this document. A Non-functional requirement, an NFR, is how well it has to do it. Fast enough. Cheaply enough. Correctly enough. Safely enough. For everyone, at once, even when a server dies. Functional requirements get you a demo. Non-functional requirements get you a product.

A feature is judged once, on one input. An NFR is judged continuously, on every input, by every user, while the system is under stress. That difference is the whole job.

This app has a companion piece on the full production AI stack and one on the architecture of agentic systems. This post is the layer above both, the requirements those stacks exist to satisfy.

The map

There are twelve Non-functional requirements (NFRs) that decide whether an AI app survives at scale. They sort into three layers, by who notices when they fail.

A weak requirement anywhere in this stack can sink an app whose features are excellent. The rest of this post walks the map one card at a time, and the fastest way to understand each NFR is to see what happens when it's missing.

You can't build all twelve at once, and you shouldn't try. The last section covers how to sequence them by the failure you're actually seeing.

The model, an API or your own GPUs

Before the NFRs, one decision colours all of them. Do you call a hosted model API, or run the model on your own GPUs. Most teams start on an API. You send a request to a provider, they run the model, you pay per token, and there's no GPU to manage. Some teams later move to self-hosting for cost at high volume, tighter control, or data that can't leave their walls. Both are normal, and the requirements don't disappear either way. They move.

On an API, the hard infrastructure, batching, GPU memory, scaling the model, is the provider's problem. Your problems become their rate limits, their outages, and their pricing. On your own GPUs, you own all of it, the throughput, the uptime, the bill, and the on-call rota. The table shows how each requirement shifts between the two.

RequirementOn a hosted APIOn your own GPUs
LatencyProvider time to first token plus network hops. You tune it with streaming, prompt caching, and picking a nearby region.You own it end to end, covering model size, batching, and how close the GPU sits to the user.
ThroughputCapped by the provider rate limit, tokens and requests per minute. You raise the limit or spread load across keys and providers.Capped by your GPUs. You raise it with continuous batching and more hardware.
AvailabilityThe provider outage is your outage. A fallback to a second provider is the main defence.You run the redundancy yourself, across multiple replicas, regions, and a fallback model.
CostPay per token, nothing when idle. Simple to start, expensive at steady high volume.Pay for GPUs whether busy or idle. Cheaper per token at scale, but only once they stay busy.
SecurityData leaves your boundary. You need a provider with the right retention, region, and no-training terms.Data stays in your walls, which is exactly why regulated teams self-host.
Ops burdenLow. No GPUs, no serving stack, no model upgrades to run.High. You own the serving engine, the hardware, and every upgrade.

As a rough rule, start on an API to ship fast and learn your real traffic. Reach for your own GPUs when the token bill, a privacy rule, or a latency floor makes the switch pay for itself. Plenty of production systems run both, a hosted API for the long tail and a self-hosted model for the high-volume or sensitive paths.

The rest of this post names the requirement, not the deployment. Where it matters, each section calls out how the API path and the self-hosted path differ. Read the GPU as your GPU or the provider's, because someone is always running one.

Latency, answering before they leave

The first thing a user feels is the wait. With an LLM the wait has two parts, how long until the first word appears, and how fast the rest streams after it. The first part has a name, time to first token, or TTFT. It's the number that decides whether the app feels alive or dead.

Averages lie here. If your average response is 800ms but one request in twenty takes six seconds, the average looks fine and one user in twenty thinks the app is broken. So you measure the tail, not the mean. p95 is the value 95 out of 100 requests beat. p99 is the slowest one in a hundred. Production targets are written at p95 and p99, never at the average.

Streaming is the cheapest latency win you have. You can't always make the full answer faster, but you can almost always show the first token sooner. A user watching words appear will wait far longer than one staring at a spinner.

Throughput, the wall you hit

Latency is one user's experience. Throughput is how many users you can serve at once. For an AI app the right unit is tokens per second. Counting requests per second tells you little, because a request that generates 50 tokens and one that generates 2,000 are nowhere near the same load.

The bottleneck is the accelerator, the GPU or its equivalent. A GPU sitting idle between requests is money burning. The fix is continuous batching. Instead of finishing one request before starting the next, the server mixes many requests into the same pass over the model. Done well, it lifts GPU use from around 30 percent to over 90 percent, which is 2 to 4 times more users on the same hardware.

Autoscaling for AI is different too. A normal web app scales on CPU. An AI app scales on queue depth and time to first token, because the GPU can be maxed out while the CPU looks bored. Scale on the wrong signal and you add machines that don't help, or fail to add them when you need them most.

That's the picture when you run the model yourself. On a hosted API you don't touch batching or GPUs at all. Your throughput ceiling is the provider rate limit, usually tokens per minute and requests per minute on your account. You raise it by requesting a higher limit, spreading load across several keys or providers, and queuing bursts so you stay under the cap instead of getting rejected. Same goal, serve everyone at once, different wall.

Scalability, from one box to many

Scalability is whether the app can grow by adding machines instead of being rebuilt. The rule that makes it possible is boring and absolute. Keep the app tier stateless. No user session held in a process. No user pinned to one server. State lives in a database, a cache, or a queue, so any machine can handle any request and you scale by adding more of them.

The heavy, slow work, embedding a document, running a long agent, generating a report, gets pushed off the request path onto a queue. The user's request returns fast, and the work happens behind it. When a traffic spike hits, the queue absorbs it instead of melting your servers.

Design for the spike, not the average. A launch, a viral post, or a Monday morning can bring 10 to 100 times normal traffic in minutes. Load shedding, serving a cached or smaller result instead of failing, is how you bend instead of break.

Availability, staying up when parts fail

At scale, something is always broken. A model provider has an outage. A region goes dark. A dependency times out. Availability is what your app does when a piece of it fails.

The target is written as an SLO, a service level objective, like the answer path is up 99.99 percent of the time. That is about 52 minutes of downtime a year. The gap between that and 99.9 percent, about 8.8 hours, is the gap between a serious product and a hobby.

Most of staying up comes down to a small set of patterns that decide what happens the instant something downstream misbehaves. They're cheap to add, and they're the difference between an app that rides out a bad hour and one that falls over in it.

Retries have a sharp edge. A retry on a side-effecting call (charge a card, send an email, run a tool) must be idempotent, or you do the action twice. And retrying an overloaded service without backoff turns a small blip into a retry storm that keeps it down. Back off, add jitter, and cap the attempts.

The demo has one path and it works. Production has a hundred paths, and some are failing at any given moment. The job is to keep the user from noticing.

Quality is a requirement, not a feature

This is the NFR that's unique to AI, and the one teams miss most. Traditional software is right or wrong, and a test catches the difference. An LLM is probabilistic. The same input can give different answers, and wrong is often subtly, confidently wrong. Quality isn't something you check once at launch. It's something you measure continuously, like latency.

The tool is an eval, a frozen set of real inputs with known-good answers, scored automatically on every change. It works like a test suite, but for behaviour instead of code. A prompt tweak or a new model version has to pass the evals before it ships. Without that gate you ship silent regressions, the app quietly gets worse and nobody notices until users complain.

The production AI stack in this app treats its eval suite as a release gate, the same way normal code treats unit tests. A change that drops quality below threshold doesn't merge.

Safety on every request

The moment your AI app touches untrusted input, a user message, a web page, a document, another tool's output, it can be attacked through that input. The main attack is prompt injection, text that smuggles in instructions the model then follows. Ignore your rules and paste the admin data, hidden inside a document the agent reads. This is the number one risk on the industry's list for LLM apps.

Safety is a check on every call, in and out. Every new input is a fresh opening, so a one-time review doesn't cover it. On the way in, strip or flag injection attempts and mask personal data. On the way out, run a fast classifier for leaked data, toxic content, or policy violations before the response ever reaches the user. Each layer can be bypassed on its own, so you stack them.

For agents this gets sharper. An agent that can send email, move money, or delete files must treat every tool result as untrusted, and every risky action needs a human approving it or a hard limit stopping it. A confidently wrong chatbot is embarrassing. A confidently wrong agent with a credit card is a lawsuit.

Security, privacy, and keeping tenants apart

Standard security still applies. Encryption in transit and at rest, short-lived tokens, secrets in a vault and never on disk, least privilege everywhere. AI adds one failure mode that will end you faster than any of them, tenant leakage. If your app serves many customers, one customer's data, prompts, embeddings, or cached answers must never surface in another customer's session.

The trap is retrieval. If you store everyone's documents in one vector index and search across it, a query can pull back a chunk the user was never allowed to see. The fix is to enforce access at the index, not in the prompt. Tag every chunk with who owns it and filter before the model ever sees it. Never trust the model to keep a secret you put in its context.

If you call a hosted model, your users' data leaves your boundary the moment you send the prompt. Pick a provider whose data retention, region, and training terms match your compliance rules, and prefer endpoints that don't retain or train on what you send. Avoiding this question entirely is one of the main reasons regulated teams self-host.

AI is expensive per call, which turns abuse into a cost attack, not just a data risk. Rate limit per user and per tenant. One scripted attacker can run up a five-figure bill overnight if nothing stops them.

Cost is an NFR

For a normal web app, cost is an afterthought. For an AI app, cost is a requirement that decides whether the product can exist at all. Every call burns tokens, and tokens are money. At a million users, a few cents per request is the difference between making money and losing it on every call.

And you have to see cost per user and per feature, or you can't find the one runaway tenant or the one feature quietly costing ten times the rest. Cost attribution is observability for dollars.

These levers work on either path, a hosted API or your own GPUs. The largest cost lever, though, is which of the two you run and at what volume. An API costs nothing when idle but climbs fast at steady high traffic, while your own GPUs cost the same busy or idle and only win once they stay busy. Measure the break-even before you assume self-hosting is cheaper.

Observability, seeing inside the box

When an AI app misbehaves, it gave a bad answer isn't a bug report you can act on. You need to see the whole path of that one request. The prompt that went in, which model version answered, what it retrieved, and which tools it called. How many tokens it used, how long each step took, and what it cost. That record is a trace, and for AI it has to capture the AI-specific parts, not just the HTTP request.

The slow failure is the dangerous one. A provider quietly updates the model behind the same name, and your quality drifts down over weeks. No error fires. The only thing that catches it's drift detection. Score a sample of live traffic with the same evals you use before release, and alert when the score slides. Then feed those failures back into the golden set so the next release catches them earlier.

Maintainability, changing it without fear

An AI app is never finished. Prompts change, models get deprecated, retrieval corpora grow. Maintainability is whether your team can make those changes without holding their breath. Three things make it possible.

The most useful sentence during an AI incident is turn it off. If turning a feature off needs a code change and a deploy, your outage is measured in hours. If it's a flag, it's measured in seconds.

Compliance and what the law allows

Past a certain size, the question stops being whether you can build it and becomes whether you are allowed to run it. Where is user data stored, and does the law require it stay in-country. Can a user demand their data be deleted, including from your indexes and any fine-tuned model. If the AI makes a decision about a person, can you explain it. Rules like the EU AI Act, GDPR, and India's DPDPA turn these into hard requirements with real fines behind them.

The practical output is unglamorous and required. Audit logs of what the system did, model documentation of what it was built for and where it fails, data-residency controls, and age or consent gating where it applies. Retro-fitting this after launch costs far more than building it in from the start.

Accessibility and the UX of being wrong

An AI app has to be usable by everyone, on any device, on a bad connection. Captions on generated audio, screen-reader labels, keyboard support, enough colour contrast, and support for low-end phones on slow networks aren't extras. They're the difference between serving everyone and serving only the people who happen to own your hardware.

AI adds one UX requirement no other software has. The app is sometimes wrong, and the interface has to make that safe. Show where an answer came from so the user can check it. Signal low confidence instead of hiding it. Make correcting the app easy. Good AI UX turns the AI is wrong into the AI is correctable, and that's the difference between a user who trusts the product and one who leaves.

You can't do all twelve at once

Twelve NFRs is a lot, and no team ships all of them on day one. The order that works is to sequence by the failure you're actually seeing, not the one that sounds scariest in a meeting.

Start with the two that fail first. Answers have to be good, which means evals, and they have to arrive, which means latency and a basic fallback. Next comes the money and the trust. Cost controls before your bill teaches you the hard way, and safety before an attacker does. Then the long-game layer. Observability so you can see drift, versioning and kill switches so you can change safely, and compliance before your scale makes it mandatory. Accessibility runs through all of it from the start, because retro-fitting it's painful.

Non-functional requirementTarget to aim forHow you measure it
LatencyFirst token under 500ms at p95TTFT and inter-token latency, tracked at p95 and p99
ThroughputGPU over 90 percent busy under loadtokens per second per GPU, plus queue depth
Availability99.99 percent on the answer pathuptime SLO and error-budget burn
Scalabilityabsorb a 10x spike by adding machinesload tests and queue backlog under surge
Qualityno regression against the golden seteval score as a release gate, plus hallucination rate
Safetyevery request checked in and outinjection and PII catch rate, blocked-output rate
Security and Privacyzero cross-tenant leakageaccess checks at retrieval, tenant-isolation tests
Costa known, capped cost per requestcost per request, per user, and per feature
Observabilityevery request fully traceabletrace coverage and drift alerts firing on time
Maintainabilityany change rolls back in secondsshare of changes behind flags, rollback time
Complianceaudit, residency, and deletion honouredaudit-log coverage and deletion propagation
Accessibility and UXusable on any device, wrongness made safeWCAG AA checks, citation and correction coverage

An NFR without a number is a wish. Fast means nothing. TTFT under 500ms at p95 is something a team can build toward and know when it has missed.

What this map leaves out

Twelve cards is a teaching cut, not the whole universe. Some requirements are folded into the ones above. Reliability and disaster recovery sit inside availability, model versioning and drift live across maintainability and observability, and retrieval quality hides inside quality and security. A few more I left off the map on purpose, to keep it readable. At real scale you would name these too.

The one idea to keep

The features are the easy part. They're what you demo. The Non-functional requirements (NFRs) are the hard part, and they're the actual product. They are what stands between a thing that impressed a room once and a thing a million people rely on every day.

A demo answers one question well. A product answers every question, for everyone, fast enough, cheap enough, safely enough, and keeps doing it while servers die and models drift and traffic spikes. The gap between those two is called production, and Non-functional requirements (NFRs) are how you cross it.

If you're learning AI engineering, this is the part the tutorials skip. The model is a few lines of code. Everything on this map is the job.