System Design Onsite / Phone Software Engineer
Create a stock-trading agent that operates as an intermediary between customers and an external broker API. It needs to:
Example requests:
BUY NFLX 24 "2024-06-14 14:30:00"
SELL INTC 15 "2024-06-14 11:45:00"
Broker APIs:
POST /orders submits a new order.GET /orders/{orderId} retrieves an order's current state.DELETE /orders/{orderId} requests cancellation of an order.Note: This is an illustrative starting point. During an interview, present your own system and justify the decisions you make, since multiple designs can be valid.
Traffic:
- Typical rate: 2,000 orders/s
- Maximum rate: 10,000 orders/s
- Orders per day: 2,000 * 86,400 = 172.8M orders/day
Storage (each order record is about 600 bytes):
- Storage added daily: ~104 GB/day
- Storage for one year: ~38 TB/year
Broker traffic (using a 90s mean fill duration and 20s polling interval):
- Concurrent active orders: 2,000 * 90 = 180,000
- Status checks: 180,000 / 20 = 9,000 GET/s
- Cancellation volume (25% expire): ~43M/day ~= 500 cancels/s average
Deadline bursts:
- Extreme case: 100,000 orders expire during one second
Make the cancellation burst at peak expiration an explicit design concern; it is frequently the most challenging aspect of this system.
Order {
id: UUID (PK)
user_id: UUID
symbol: String
side: Enum(BUY, SELL)
quantity: Int
order_type: Enum(MARKET, LIMIT)
limit_price: Decimal (optional)
deadline_at: Timestamp
status: Enum(SUBMITTED, PENDING, PARTIALLY_FILLED, FILLED, CANCELED, EXPIRED, FAILED)
broker_order_id: String (nullable until submitted)
filled_quantity: Int
created_at: Timestamp
updated_at: Timestamp
version: Int
}
OrderEvent {
id: UUID (PK)
order_id: UUID (FK)
event_type: Enum(CREATED, SUBMITTED_TO_BROKER, STATUS_UPDATE, CANCELED, EXPIRED)
prev_status: Enum
new_status: Enum
metadata: JSON
created_at: Timestamp
}
IdempotencyKey {
key: String (PK)
order_id: UUID
created_at: Timestamp
expires_at: Timestamp
}
Start with this lean interview schema, and introduce extra attributes only when discussing particular concerns, such as retry handling or reconciliation.
REST works well for the client-facing interface because it maps naturally to order CRUD operations. Use asynchronous events internally so that scheduling and broker communication are not tightly coupled.
POST /v1/orders
Request:
{
"idempotency_key": "string",
"user_id": "string",
"side": "BUY" | "SELL",
"symbol": "string",
"quantity": 12,
"order_type": "MARKET" | "LIMIT",
"limit_price": 87.25,
"deadline_at": "2026-04-18T16:30:00Z"
}
Response:
{
"order_id": "uuid",
"status": "SUBMITTED",
"submitted_at": "timestamp"
}
GET /v1/orders/{order_id}
Response:
{
"order_id": "uuid",
"status": "PENDING",
"filled_quantity": 0,
"deadline_at": "timestamp",
"updated_at": "timestamp"
}
DELETE /v1/orders/{order_id}
Response:
{
"order_id": "uuid",
"status": "CANCELED"
}
order.created
order.submitted_to_broker
order.status_updated
order.canceled
Idempotency keys are essential for retry-safe submission. Persist each key with a TTL, and when the same key is submitted again, return its existing order_id.
High-Level System Architecture
Broker API
Asynchronous Services
Message Transport
Data Stores
Application Layer
Edge Layer
Clients
Client A
Client B
Client N
Load Balancer
Order API Authentication + Rate Limiting
PostgreSQL Orders + Events
Redis Cache + ZSET
Kafka or SQS Order Events
Broker Integration
Status Poller
Deadline Scheduler
Reconciliation Job
POST /orders
GET /orders/{id}
DELETE /orders/{id}
POST /v1/orders along with an idempotency key.order.created onto the queue.order_id to a Redis ZSET whose score is deadline_at.order.created, invokes broker POST /orders, and changes the order state to PENDING.GET /orders/{id}, records the returned state, and removes completed orders from the ZSET.DELETE /orders/{id} to cancel them.Apply the outbox pattern so saving an order and emitting its event occur atomically. This avoids losing events if a process crashes.
Approach:
order_id so the load is distributed.Avoid retrying cancellation requests forever. During a broker outage, unlimited retries can overwhelm the broker and delay recovery.
| Decision | Benefit | Cost |
|---|---|---|
| Redis ZSET for deadlines | O(log N) range queries | It is volatile and must be reconstructed from the database |
| Polling vs webhooks | Easier integration | Increased broker request volume |
| Strong consistency on submit | More accurate status for clients | Higher latency and database pressure |
| Queue-based broker calls | More even load | Additional operational complexity |
Omitting idempotency can create duplicate orders when clients retry. Require a client idempotency key in every case.
Failing to account for partial fills can result in invalid cancellation attempts. Check the latest state immediately before issuing a cancellation.
| Aspect | Solution |
|---|---|
| Deadline tracking | Redis ZSET with schedulers partitioned by shard |
| Broker integration | Asynchronous workers with a circuit breaker |
| Consistency | Broker authority combined with reconciliation |
| Reliability | Outbox pattern plus a durable database |
| Spike handling | Throttled cancellation workers with jitter |