Let's design for 10 million Monthly Active Users (MAU) with 2 million Daily Active Users (DAU)
If each user does ~30 requests/day (logging exercises, checking progress, etc.):
Storage:
Assuming 20 requests are about logging exercises
5-year projection would be ≈ 25 TB *
POST /usersCreate a new user.
Body
{
"name": "string",
"email": "string"
}
Status codes
| Code Meaning | |
| 201 | Created successfully |
| 400 | Invalid body (missing/malformed fields) |
| 409 | Email or name already exists |
| 500 | Server error |
GET /users/{id}Fetch a single user.
Response 200
{
"id": 0,
"name": "string",
"email": "string"
}
Status codes
| Code Meaning | |
| 200 | Returns the user |
| 404 | User not found |
| 500 | Server error |
PUT /users/{id}Replace a user's data.
Body
{
"name": "string",
"email": "string"
}
Response 200 — returns the updated user (same shape as GET /users/{id})
Status codes
| Code Meaning | |
| 200 | Returns the updated user |
| 400 | Invalid body |
| 403 | Not authorized |
| 404 | User not found |
| 500 | Server error |
GET /users/{id}/achievementsQuery parameters
| Param Type Description | ||
from | timestamp | Start of date range |
to | timestamp | End of date range |
term | string | Search achievements by name |
page | integer | Page number |
limit | integer | Page size |
Response 200
{
"achievements": [
{
"name": "string",
"description": "string",
"startAt": "ISO timestamp",
"endAt": "ISO timestamp",
"progress": 0
}
],
"page": 0,
"total": 0
}
Status codes
| Code Meaning | |
| 200 | Success |
| 404 | User not found |
| 500 | Server error |
GET /users/{id}/statsQuery parameters
| Param Type Description | ||
from | timestamp | Start of date range |
to | timestamp | End of date range |
Response 200
{
"exercises": 0
//...
}
Status codes
| Code Meaning | |
| 200 | Success |
| 404 | User not found |
| 500 | Server error |
GET /activitiesQuery parameters
| Param Type Description | ||
from | timestamp | Start of date range |
to | timestamp | End of date range |
term | string | Search activities by name |
page | integer | Page number |
limit | integer | Page size |
Response 200
{
"activities": [
{
"id": 0,
"name": "string",
"loggedAt": "timestamp",
"type": "RUNNING | SWIMMING | ...",
"exercises": 0
}
],
"page": 0,
"total": 0
}
Status codes
| Code Meaning | |
| 200 | Success |
| 500 | Server error |
POST /activitiesBody
{
"name": "string",
"loggedAt": "timestamp",
"type": "RUNNING | SWIMMING | ..."
}
Status codes
| Code Meaning | |
| 201 | Success |
| 400 | Invalid timestamp format, type, etc. |
| 500 | Server error |
GET /activities/{id}Response 200
{
"id": 0,
"name": "string",
"loggedAt": "timestamp",
"type": "RUNNING | SWIMMING | ...",
"exercises": [
{
"name": "string",
"attributes": {
"key": "value"
}
}
],
"attributes": {
"key": "string | number | bool"
}
}
Status codes
| Code Meaning | |
| 200 | Success |
| 404 | Activity not found |
| 500 | Server error |
PATCH /activities/{id}All fields optional; only defined fields are updated.
Body
{
"name": "string",
"loggedAt": "timestamp",
"type": "RUNNING | SWIMMING | ...",
"exercises": [
{
"name": "string",
"attributes": {
"key": "value"
}
}
],
"attributes": {
"key": "string | number | bool"
}
}
Status codes
| Code Meaning | |
| 200 | Returns the updated activity |
| 400 | Invalid body |
| 404 | Activity not found |
| 500 | Server error |
DELETE /activities/{id}Status codes
| Code Meaning | |
| 204 | Deleted successfully (no content) |
| 404 | Activity not found |
| 500 | Server error |
Note: the original draft listed201for delete —204 No Contentis the conventional code for a successful delete with no response body.
POST /activities/{id}/exercisesBody
{
"name": "string",
"attributes": {
"key": "value"
}
}
Status codes
| Code Meaning | |
| 201 | Created successfully, returns the exercise |
| 400 | Invalid body |
| 404 | Activity not found |
| 500 | Server error |
PUT /activities/{id}/exercises/{eid}Body
{
"name": "string",
"attributes": {
"key": "value"
}
}
Status codes
| Code Meaning | |
| 200 | Returns the updated exercise |
| 400 | Invalid body |
| 404 | Activity or exercise not found |
| 500 | Server error |
DELETE /activities/{id}/exercises/{eid}Status codes
| Code Meaning | |
| 204 | Deleted successfully (no content) |
| 404 | Activity or exercise not found |
| 500 | Server error |
GET /goalsQuery parameters
| Param Type Description | ||
from | timestamp | Start of date range |
to | timestamp | End of date range |
term | string | Search goals by name |
page | integer | Page number |
limit | integer | Page size |
Response 200
{
"goals": [
{
"id": 0,
"name": "string"
}
],
"page": 0,
"total": 0
}
Status codes
| Code Meaning | |
| 200 | Success |
| 500 | Server error |
POST /goalsCreate a new goal.
Body
{
"name": "string",
"description": "string",
"startAt": "ISO timestamp",
"endAt": "ISO timestamp",
"target": 0
}
Adjust fields to match whatever attributes a goal actually tracks (e.g. target value/metric, linked activity type).
Status codes
| Code Meaning | |
| 201 | Created successfully, returns the goal |
| 400 | Invalid body (bad timestamp, missing fields, etc.) |
| 500 | Server error |
GET /goals/{id}Fetch a single goal.
Response 200
{
"id": 0,
"name": "string",
"description": "string",
"startAt": "ISO timestamp",
"endAt": "ISO timestamp",
"target": 0,
"progress": 0
}
Status codes
| Code Meaning | |
| 200 | Returns the goal |
| 404 | Goal not found |
| 500 | Server error |
PATCH /goals/{id}All fields optional; only defined fields are updated.
Body
{
"name": "string",
"description": "string",
"startAt": "ISO timestamp",
"endAt": "ISO timestamp",
"target": 0
}
Status codes
| Code Meaning | |
| 200 | Returns the updated goal |
| 400 | Invalid body |
| 404 | Goal not found |
| 500 | Server error |
DELETE /goals/{id}Status codes
| Code Meaning | |
| 204 | Deleted successfully (no content) |
| 404 | Goal not found |
| 500 | Server error |
Sync and event flow
Clients and wearables reach the system through a load balancer, which forwards everything to a gateway. The gateway rate-limits and routes requests. Reads (a user's stats, a goal's current progress) go straight to the primary database. Writes to activities and exercises get published as events to a log (Kafka) instead of being applied inline, and everything downstream reacts to that log rather than to a direct call.
Three consumers read the same stream independently:
The achievement service is a separate consumer that listens for goal-completed events and awards achievements. It doesn't sit inside the goal processor: goal logic shouldn't need to know what an achievement is, and adding a new achievement rule shouldn't touch the goal path at all.
Anything worth surfacing to a client, goal progress, a completed goal, a new achievement- becomes an event the notification service picks up. That service writes to Redis and pushes to the user's device. Redis does two jobs here: it's the cache backing activity reads, and it's the store behind the SSE connections clients hold open for live updates.
Wearables don't write to the event log directly. A wearable calls a webhook through the load balancer, and that call triggers a sync for the user rather than being trusted as data on its own. The event log's shape stays controlled by the gateway, not by whatever a given vendor decides to send.
Clients also pull activities on a schedule through the load balancer, independent of SSE and webhooks. SSE tells a client something changed, but connections drop, and webhook calls get missed, so periodic pull is the fallback that guarantees convergence. Two devices can edit the same activity while offline, so conflict resolution uses operation-based CRDTs rather than last-write-wins: each device's edits are a stream of operations, and merging those streams reaches the same state everywhere without a central lock.
Define the data model. Identify the main entities, their attributes, and relationships. Consider the choice of database type (SQL vs NoSQL) and justify your decision based on access patterns...
Deep dive into 2-3 key components. Explain how they work, how they scale, discuss tradeoffs, capacity, and any relevant algorithms or data structures.