The problem
You are designing the backend for a stock trading app that allows users to place buy and sell orders in real time. The system must process orders, update portfolios, and show users the latest prices and their order status instantly. If the design is wrong, users may see stale data, miss trades, or experience slowdowns during market surges.
You are the backend engineer responsible for the core trading order flow.
If the system cannot handle peak loads or fails to update trades accurately, users may lose money or trust, and regulatory compliance may be at risk.
Must hold true
- Order placement and status updates must be reflected to users within 1 second.
- The system must handle at least 10,000 concurrent users during market open.
- Order data must be durable and never lost.
- Only authenticated users can place or view orders.
Functional Requirements
What the system must DO.
| Requirement | Priority |
|---|---|
| Users must be able to place buy and sell orders for stocks. | Must have |
| Users must be able to view their open and completed orders. | Must have |
| Order status must update in real time as trades execute. | Must have |
| Only authenticated users can place or view orders. | Must have |
| Users should see up-to-date stock prices when placing orders. | Nice to have |
Non-Functional Requirements
How well it must do it — latency, availability, scale. Measurable, not vibes.
| Requirement | Priority |
|---|---|
| Order placement and status updates must complete within 1 second. | Must have |
| Order data must be durable and never lost. | Must have |
| The system must handle at least 10,000 concurrent users during peak times. | Must have |
| The system should be available 99.9% of the time during trading hours. | Nice to have |
What you learn by asking
In an interview these facts are NOT volunteered — the candidate earns them with clarifying questions. These are the answers behind this design.
| If you ask about… | The answer |
|---|---|
| peak concurrent users | The system must support 10,000 concurrent users during market open. |
| order rate | At peak, users place up to 2,000 orders per second. |
| average order size | Each order record is approximately 500 bytes. |
| order history retention | Order data must be retained for at least 7 years for compliance. |
| read/write ratio | Reads (order status checks and history views) outnumber writes (order placements) by about 10 to 1. |
| stock price update frequency | Stock prices update every 1 second for each symbol. |
Capacity Estimation
Rough arithmetic turns the requirements into engineering decisions — it tells you which parts need scaling machinery and which are trivially easy. Only the order of magnitude matters.
| Quantity | Answer | How it is derived |
|---|---|---|
| Peak order status read QPS | ~20 reads/sec | If there are 10,000 concurrent users and each checks their order status once per second, that's 10,000 reads/sec. But with a read/write ratio of 10:1 and 2,000 writes/sec, estimated reads/sec = 2,000 × 10 = 20,000 reads/sec. |
| Annual storage for orders | ~31.5 GB | 2,000 orders/sec × 500 bytes × 3,600 sec/hr × 7 hr/day × 250 days/year = 2,000 × 500 × 3,600 × 7 × 250 = 6.3 × 10^12 bytes/year ≈ 6.3 TB/year. But for just the orders, 2,000 × 500 × 3,600 × 7 × 250 / 1,073,741,824 ≈ 31.5 GB/year (assuming only order metadata is stored). |
| Total storage for 7 years | ~220 GB | 31.5 GB/year × 7 years ≈ 220 GB. |
Quick check
Without scrolling back: roughly what did we work out for peak order status read qps?
The architecture, built up
Nobody designs the final diagram in one stroke, so this design is built up one component at a time rather than shown finished. Start with the simplest thing that works, then add each piece and see where it attaches.
Each stage lets the numbers break the design, then fixes exactly one bottleneck with exactly one new component — and at each break you get a chance to call the fix yourself before it is revealed. You can stop reading after any stage and still hold a complete, working system in your head — that is the point.
The simplest thing that works
Start with a client talking to a single API server, which reads/writes to a single order database.
This initial design is the smallest system that can serve requests: clients send order requests to an API server, which authenticates, processes, and persists orders to a database.
All reads and writes go through the API server, which directly queries or updates the database.
The design so far
Where it breaks
At 10,000+ concurrent users and 22,000 QPS, a single API server is overwhelmed.
Quick check
Which single component would you add to fix that?
One API server cannot handle 10,000+ concurrent users
The fix: Add a load balancer to distribute requests across multiple API servers, enabling horizontal scaling.
The load balancer acts as the entry point, distributing incoming requests to multiple API servers.
This allows the system to scale horizontally by adding more servers as needed, preventing a single point of failure.
The design so far
Where it breaks
At 20,000 reads/sec, the database becomes a bottleneck for order status lookups.
Quick check
Which single component would you add to fix that?
Database load spikes due to high read volume
The fix: Introduce a cache—a fast in-memory store—for order status, reducing database load.
A cache stores the most frequently accessed order statuses, allowing the API server to serve most reads without hitting the database.
This improves response time and reduces database load, especially for hot orders being checked repeatedly.
The design so far
Where it breaks
Stock prices update every second; users need up-to-date prices when placing orders.
Quick check
Which single component would you add to fix that?
Users see stale or missing stock prices
The fix: Add a dedicated price service to fetch and serve real-time stock prices to the API server.
A specialized price service ingests real-time market data and provides up-to-date quotes to the API server.
This keeps price logic separate from order processing and ensures users always see current prices.
The finished design
The full reference design, with the reason behind every connection. When you practice, you will rebuild this from a palette that includes decoys.
Why each connection exists
- Client AppLoad BalancerClients connect to the load balancer for high availability and scaling.
- Load BalancerTrading API ServerLoad balancer distributes requests to API servers.
- Trading API ServerOrder Status CacheAPI server checks and updates the cache for fast order status.
- Order Status CacheOrder DatabaseIf the order status is not in cache, fetch from the database.
- Trading API ServerOrder DatabaseAll new orders and updates are persisted to the database.
- Trading API ServerStock Price ServiceAPI server queries price service for real-time stock quotes.
The design in one glance
Each stage fixed exactly one problem. If you can retell this table from memory, you can derive the whole architecture on a whiteboard.
| The design so far | What broke | The fix |
|---|---|---|
| The simplest thing that works | — the starting point | Start with a client talking to a single API server, which reads/writes to a single order database. |
| One API server cannot handle 10,000+ concurrent users | At 10,000+ concurrent users and 22,000 QPS, a single API server is overwhelmed. | Add a load balancer to distribute requests across multiple API servers, enabling horizontal scaling. |
| Database load spikes due to high read volume | At 20,000 reads/sec, the database becomes a bottleneck for order status lookups. | Introduce a cache—a fast in-memory store—for order status, reducing database load. |
| Users see stale or missing stock prices | Stock prices update every second; users need up-to-date prices when placing orders. | Add a dedicated price service to fetch and serve real-time stock prices to the API server. |
Good to know — What this design leaves out — on purpose
Every real system also needs monitoring and metrics, logging and tracing, alerting and on-call, CI/CD, auth hardening and abuse limits, and a cost model. None of them appear above, and that is a choice: they sit beside EVERY system in much the same shape, so drawing them here would add the same five boxes to every problem in this library while crowding out the part that is actually specific to this one — the path a request takes and where it breaks. This is also why a metrics or monitoring block is a wrong answer in the practice canvas: it is never the thing that makes this design work. In a real interview, name these in one sentence once the data path is settled — "I would put metrics on the cache hit rate and alert when it drops" — and move on. Reaching for them before the core flow is drawn reads as avoiding the question.
API Design
Every operation traces back to a requirement — nothing extra, nothing missing.
| Endpoint | Purpose |
|---|---|
| POST /orders | Submit a new buy or sell order. |
| GET /orders | Retrieve a user's open and completed orders. |
| GET /orders/{orderId} | Get the status of a specific order. |
| GET /prices/{symbol} | Fetch the latest price for a stock symbol. |
Quick check
Which one would you call to submit a new buy or sell order?
Data Model
| Entity | Fields |
|---|---|
| Order | orderId: string (Unique order identifier), userId: string (ID of the user placing the order), symbol: string (Stock symbol), side: string ('buy' or 'sell'), quantity: int (Number of shares), price: float (Order price), status: string (Order status (open, filled, cancelled, etc.)), createdAt: datetime (Order creation time), updatedAt: datetime (Last status update) |
Defending the Design
Interviewers close by stress-testing the design. Strong answers follow one shape: which component → what breaks there → the fix and its cost. Here are this problem's probes and the points a strong answer hits:
What bottlenecks could arise in your order status cache, and how would you address them? Cache size limits could cause evictions and increase DB load.. A single cache node could be overwhelmed—use partitioning or replication.. Cache consistency: stale data if not updated on every write..
How does your design ensure order data durability if the main order database fails? Use database backups and replication for durability.. Persist orders before acknowledging to the user.. Consider failover strategies to a standby DB..
How does the load balancer help the system scale to 10,000+ concurrent users? Distributes traffic across multiple API servers.. Prevents any single server from becoming a bottleneck.. Enables horizontal scaling by adding more servers..
What are the tradeoffs between using a cache versus a read replica for order status reads? Cache provides faster response for hot data, but needs consistency management.. Read replicas scale reads but have replication lag and higher cost.. Cache reduces DB load for frequent lookups; replicas help with large, less predictable read patterns..
Tip — Key takeaway
A robust trading backend must balance low-latency order processing, high throughput, and strong data durability. Scaling for peak load requires both horizontal scaling (load balancer, multiple servers) and vertical scaling (caching hot data). Every component must be justified by concrete requirements and traffic patterns.
Common trap — Common mistakes on this problem
Relying on a single API server or database, which cannot handle peak load or ensure high availability. · Using a queue for order placement when synchronous processing is required for instant feedback. · Adding a search index or read replica without real need, increasing complexity and cost.
You've seen the whole design — now build it yourself
The practice run walks the same steps, but YOU do the work: gather the requirements, run the numbers, wire the architecture — with an AI interviewer and per-step feedback.