0) Problem Restatement
Meta asked: design the backend of a fitness tracking app. Users record workouts (runs, rides, walks) with a phone or watch: GPS route, time, distance, pace, heart rate. They see their history and statistics (weekly distance, personal records), follow friends and see an activity feed, and optionally compete on segments (popular stretches of road with leaderboards).
1) Requirements
- Upload workouts (maybe recorded offline) with GPS points every second and sensor data.
- Show a workout: map, splits, charts. Show history and totals.
- Social: follow, feed, kudos and comments.
- Segments and leaderboards (optional).
- Privacy: hide the start and end near home (privacy zones), and private activities.
1.1 Scale
- 50M users, 5M workouts/day. A 1-hour workout = 3,600 GPS points ≈ 50–100 KB compressed → ~500 GB/day of route data.
2) Architecture
Architecture Diagram
flowchart LR
DEV["Phone / watch - records offline"] -->|"upload file (FIT/GPX/JSON)"| API["Upload API"]
API --> OS[("Object storage - raw files")]
API --> Q[("Processing queue")]
Q --> PROC["Processor - clean, stats, privacy zones"]
PROC --> DB[("Activities DB - summary")]
PROC --> RT[("Route store - encoded polyline")]
PROC --> SEG["Segment matcher"]
SEG --> LB[("Leaderboards")]
PROC --> K[("Activity events")]
K --> FEED["Feed fan-out"]
FEED --> FS[("Feed store")]
APP["App"] --> RAPI["Read APIs"]
RAPI --> DB
RAPI --> RT
RAPI --> FS3) Data Model
activities: activity_id, user_id, type, start_time, duration_s, distance_m, elevation_m,
avg_pace, avg_hr, visibility, summary_polyline, created_at
routes: activity_id → full-resolution encoded polyline + sensor streams (object storage / blob)
stats: user_id, week, distance_m, time_s, count (pre-aggregated)
follows: follower_id, followee_id
- Summary data (small) goes in the DB for lists and feeds. Full streams (big) go in object storage, loaded only when the user opens the activity.
- Routes are stored as an encoded polyline (a compact text encoding of coordinates), plus a simplified version for thumbnails.
4) Key Flows
- Upload: the device records locally (works offline) and uploads the file when connected, with an idempotency key (the device's activity UUID) so retries don't duplicate. The API stores the raw file and queues processing, then returns right away.
- Processing: parse, smooth GPS noise, compute distance, pace, splits and elevation, apply privacy zones (trim points near home), save the summary and streams, update weekly stats, and match segments.
- Feed: publish "activity created". Fan out to followers' feeds (on write for normal users, on read for accounts with huge followings).
5) Segments and Leaderboards
- A segment is a polyline with a start and end. For each new activity, find candidate segments near its route (a geospatial index on segment bounding boxes), then check whether the route passes the start and end in order, and compute the elapsed time.
- Leaderboards per segment: a sorted set by time (overall, by age group, and friends).
6) Wrap-Up
Devices record offline and upload whole activity files idempotently. An async processor cleans GPS, computes stats, applies privacy zones, stores small summaries in the DB and full streams as encoded polylines in object storage, and updates pre-aggregated weekly stats. Activity events drive feed fan-out (hybrid for popular users), and a geospatial segment matcher updates sorted-set leaderboards.