How to read this. This page is the 45 minutes, in the order I would spend them. It uses a run recorder as the example, and the same design fits a hike, a ride or a trip log. The follow-up questions are my reconstruction from public prep material, not transcripts of real rounds.
By the end you can:
- Say why the recorder cannot live in a screen, and what owns it instead.
- Explain what you do when the app is killed in the middle of a run.
- Say why a 10 km run can read as 13 km, and what you do about it.
Read section 4.2 twice. It is the part most answers miss.
TL;DR. My answer: the phone is a hostile place to record for an hour, so the recording must not depend on the screen or on the app staying alive. A small recorder (a foreground service on Android, a background activity session on iOS) owns the location stream. It saves every point to a local database in small batches, and the screen only reads that database. If the app is killed, the next start finds a recording that still says RECORDING and offers to resume it. I keep the raw points and work out distance later, because GPS is noisy and I want to be able to fix the maths.
Patterns in this round.
- State machine (a recording has a short list of states)
- Outbox (the finished run waits on the phone until it is uploaded)
- Idempotency key (the recording id makes an upload safe to repeat)
- Scoped owner (the recorder lives as long as the recording, not the screen)
More in design patterns in mobile rounds.
1. Scope and constraints (minutes 0 to 4)
In short: Record one run: start, pause, stop, see the route and distance live, and save it. Sharing, route planning and offline maps wait.
A prompt I would expect: "Design an app that records a run." I ask five questions, with a default for each:
- How long is a recording? Up to 3 hours, with the screen locked most of the time.
- What do we show live? The route, distance, time and pace. Heart rate and a watch wait.
- Do we have a backend? Yes. It stores finished runs for the user's account.
- Offline? Recording works with no signal. Upload waits for the network.
- How exact must the distance be? Close enough that a runner trusts it. I will say what that means.
| In scope | Out of scope | Left out until you ask |
|---|---|---|
| Start, pause, resume, stop | Map tiles and map style | Live sharing with friends |
| Recording with the screen locked | Training plans | Route planning |
| Distance, time and pace | Social feed | Heart rate and a watch |
| Saving and uploading a finished run | Maps with no network | Auto-pause |
The phone does four things to this design:
| What happens | Requirement at risk | Deep dive |
|---|---|---|
| The screen locks and the app goes to the background | Points keep arriving for the whole run | Sections 3 and 4.1 |
| The system kills the app, or the user removes permission | A run is not lost | Section 4.2 |
| GPS is noisy: bad fixes, jumps, drift when standing still | The distance is believable | Section 4.3 |
| Location is a battery cost for the whole hour | The phone lasts the run | Sections 2 and 4.3 |
Your decision. The interviewer says: "Also add live sharing, route planning and a smartwatch." Which do you keep?
First idea, and why it fails. Say yes to all three. Each one is a second system, and time runs out before the "the app was killed in the middle of the run" path, which is the real hard part.
What I would say.
Say this. "Recording with the screen locked, a run that survives a kill, and a believable distance are in. Live sharing, route planning and a watch each add a system of their own, so they wait. Tell me if you want one earlier."
2. Data and contracts (minutes 4 to 14)
In short: Two local tables: the recording, and its raw points, written in small batches. The server gets the finished run once, under its recording id.
Your decision. Where does the route live while the run is going on? In memory, in a file you write at the end, or in a database as the points arrive?
First idea, and why it fails. A list in memory, saved when the user taps Stop. It is the shortest code. It also means a kill, a crash or a dead battery in minute 50 loses all 50 minutes. Android can stop an app in the background, and Apple says the system can terminate an app at any time.
What I would say. The phone keeps two tables:
- Recordings: one row per run. Columns: recording id (a UUID made on the phone), state (recording, paused, finished, uploaded), start time, end time, and the current segment number.
- Points: one row per location fix. Columns: recording id, segment, sequence number, time, latitude, longitude, accuracy, and speed if the phone gives it. The key is (recording id, sequence number). Rows are only ever added, never edited.
The recorder saves points in small batches in one transaction, for example every 10 seconds. A crash can lose only the last batch. The screen reads the database and never keeps the route in memory only. I store the raw points and work out distance and pace from them, so I can fix the maths later without losing data.
The contract with my server has two calls:
- Upload a run: the recording id, the summary, and the points in chunks of a few hundred. Each chunk has a chunk number.
- List my runs: the finished runs for this account, newest first.
The recording id is made on the phone, so a retry sends the same id. The server uses it as a unique key, as in the idempotency keys guide, and a second send of the same chunk changes nothing.
The follow-up question I expect:
"Why not upload points live, as they arrive?"
I can add it as a second path, but it cannot be the only copy. With no signal on a trail, live upload stalls, and I would still need the local database to survive. So the phone is the first copy and the server is the second.
These are my own assumptions, not measurements:
| Assumption | Arithmetic | What it decides |
|---|---|---|
| One fix a second for a run. About 40 bytes a point: two doubles for latitude and longitude, a time, an accuracy, a speed and a sequence number | 1 hour = 3,600 points. 3,600 x 40 bytes = 144,000 bytes, about 144 KB | A whole run is small enough to keep and upload as is |
| A 3 hour ride at one fix a second | 3 x 3,600 = 10,800 points. 10,800 x 40 bytes = 432,000 bytes, about 432 KB | Still small. Even long recordings do not need compression to start |
| Batch writes every 10 seconds | 10 points a batch. 3,600 / 10 = 360 database writes an hour | A crash loses at most 10 seconds. Cheap on the battery |
| 100,000 runs a day, each about 144 KB | 100,000 x 144 KB = 14,400,000 KB, about 14.4 GB a day | Upload on Wi-Fi when possible, and in chunks that can resume |
3. Client structure (minutes 14 to 22)
In short: A recorder that is not a screen, a database in the middle, and screens that only read.
Your decision. Who owns the location stream? The recording screen, or something that lives as long as the recording?
First idea, and why it fails. The screen's ViewModel (the class that holds one screen's state) asks for location updates while it is visible. The user locks the phone and the updates stop. Android says a background app can receive location updates only a few times each hour, so a run recorded this way has a hole in it. On iOS, Apple says the system suspends most apps shortly after they move to the background, and a suspended app does not receive location updates.
What I would say. A recorder owns the stream, and it has two platform shapes:
- Android: a foreground service with
foregroundServiceType="location", which shows a notification while it runs. The manifest also needsFOREGROUND_SERVICE_LOCATION, and the app must have the location permission before the service starts. Android's docs say the system treats location access as foreground when an activity is visible or a foreground service is running. - iOS: the Background Modes capability with Location updates, a
CLBackgroundActivitySessionand aCLServiceSession, both created while the app is in the foreground. If the app is terminated and relaunched in the background, the session must be created again right away.
The recorder does three things only: ask the platform for fixes, drop clearly bad ones, and save batches to the database. The screens (live map, stats, the finished run) read the database and show it. The recorder is the scoped owner of one recording, and the screens are only viewers.
Say this. "A recorder owns the location stream, a foreground service on Android and a background activity session on iOS. It saves points to a local database in small batches, and every screen reads from that database."
The follow-up question I expect:
"Does the app need the Always or background location permission?"
Not for this design, as I read the docs. The user taps Start while the app is open, and the recorder is a foreground service or a background activity session started from there. Android's docs say you cannot start a location foreground service from the background unless the app has the background permission, so I start it only from the visible app and avoid asking for "Allow all the time". That is easier for the user to accept, and Google Play limits background location to apps that need it for their core features. Apple says a background activity session also gives When in Use authorization. This is my reading of the docs, and I would test it on a real device on each OS version.
4. Make the hard part work (minutes 22 to 40)
In short: A recording is a small state machine on disk. Start only from the visible app, expect a kill, and treat every GPS point as a suspect.
4.1 Start, and keep recording with the screen locked (minutes 22 to 29)
Your decision. The user taps Start. What has to be true before the first point is saved, and what keeps the stream alive after the screen locks?
First idea, and why it fails. Request the permission, start listening, and hope. Permission can be missing or only approximate, location can be switched off, and the service may not be allowed to start. The first point never arrives and the user runs for 40 minutes before noticing.
What I would say. Check, then start, then confirm:
- Check. Is location on? Is the permission granted? On Android 12 and newer the user can grant only approximate location (Android says about 3 square kilometres, against about 50 metres or better for precise). A run needs precise, so ask for it again with a clear reason, or refuse to start.
- Start. Write the recording row first with the state RECORDING. Start the foreground service from the visible app. Show the notification with a Stop button.
- Confirm. The first saved point proves it works. If no fix arrives within a short time, say so on screen: "Waiting for GPS".
Battery Saver also matters. Android's docs say that if getLocationPowerSaverMode() returns LOCATION_MODE_FOREGROUND_ONLY, the app keeps receiving updates while it runs a foreground service, even with the screen off. That is one more reason to be a foreground service and not a background task.
Say this. "Start is a check, then a recording row, then the service. The service runs with a notification, so the screen can lock. A recording has states, and every state is on disk."
The follow-up question I expect:
"How often do you ask for a location?"
As rarely as the sport allows. A run needs about one fix a second to draw a smooth line. A hike can use one every 5 seconds. A ride at speed needs more spread by distance than by time. I would ask the location library for balanced power and a minimum distance between updates, and let the system batch fixes when the screen is off. I have not verified the exact request options here, so I would read the current API reference before choosing values.
4.2 The app was killed in the middle of the run (minutes 29 to 35)
Your decision. At minute 50 the system kills the app. What does the user see when they open it again?
First idea, and why it fails. A new empty screen, because the route was in memory. Or a half-state where the app thinks it is recording but nothing runs, and the runner stops a run that was not recording for the last 10 minutes.
What I would say. The database remembers. On every app start the app asks one question: is there a recording whose state is RECORDING while no recorder is running?
- If yes: show the run so far, with the time of the last point. Offer "Resume" and "Finish".
- Resume: start a new segment. The old segment ends at the last saved point, and the new one begins at the first new point. Never draw a straight line across the gap, and never add the gap to the distance.
- Finish: close the recording at the last point. The user keeps a shorter run, not nothing.
The same path covers the other interruptions. If the user turns location off or takes the permission away, the recorder marks the gap and shows a clear message, and the moment location is back it continues in a new segment. If the battery gets very low, the app can stop cleanly, save, and say so. I would not rely on the system restarting the service after a kill, because I have not verified that behaviour.
Say this. "A kill is expected. The points are on disk in small batches, the recording still says RECORDING, and the next start finds it. The user can resume in a new segment or finish what we have. The gap is marked and never counted."
The follow-up question I expect:
"The user ignores the prompt and starts a new run. What now?"
Two recordings in RECORDING would confuse everything, so the app allows one. Starting a new run first asks about the old one: finish it, or discard it. The old one is never deleted without the user saying so.
4.3 Noisy points and a believable distance (minutes 35 to 40)
Your decision. You have 3,000 points for a 10 km run. How do you get the distance?
First idea, and why it fails. Add up the straight-line distance between each pair of points. GPS is not exact, so every point is a little off. At one point a second, a runner at 5 minutes a kilometre moves 3.3 m between points. If each segment gains only 1 m of noise, 3,000 segments x 1 m = 3,000 m, which is 3 km on top of 10 km. The run reads as 13 km.
What I would say. Clean the points, and do it after the fact, from the raw ones:
- Drop bad fixes. Each fix comes with an accuracy estimate. Drop points whose accuracy is worse than a limit, for example 50 metres. That is my starting number, to tune with real runs.
- Drop impossible jumps. If the speed between two points is not possible for the sport (for a run, faster than a person can sprint), skip the point.
- Count a new point only after a real move. Add distance only when the new point is further from the last counted point than the noise, for example 5 metres. Standing at a traffic light then adds nothing.
- Never cross a gap. Distance is the sum inside each segment.
Because the raw points are on disk, I can change any of these numbers later and recompute old runs. If I had saved only a running total, a bug in the first version would stay in every run forever.
Say this. "I keep raw points and work out distance from them. I drop low-accuracy fixes and impossible jumps, I only count a move that is bigger than the noise, and I never add a gap. The numbers are starting values that I tune with real runs."
The follow-up question I expect:
"Why not use the speed the phone gives you, instead of positions?"
It is a good second signal. The platform often reports speed with each fix, and speed is smoother than position. But it can be missing or wrong when the signal is poor, so I use it to check positions, not to replace them.
5. Break it, ship it, roll it back (minutes 40 to 45)
In short: Each failure ends in a run the user can keep, with the gap shown. Never a lost run and never a silent hole.
| Failure | User sees | What holds the truth | Recovery | Basis |
|---|---|---|---|---|
| App killed mid-run | "Resume or finish" | Points in the phone database, row says RECORDING | Next start finds it, new segment, gap marked | Apple documents that the system can terminate an app at any time. Android process death is well known, but I did not fetch a page for it. Recovery is my design |
| Location off or permission removed | A clear message, a gap | The saved points so far | Continue in a new segment when it is back | My design |
| Only approximate location on Android 12+ | "Precise location needed" | Permission state | Ask again with a reason, or do not start | Approximate vs precise: documented. The rest is my design |
| Bad GPS (tunnel, tall buildings) | A smooth line, no spikes | Raw points, accuracy per fix | Filter at read time, can be changed later | My design |
| Screen locked for 2 hours | The full route | The foreground service or background session | Nothing to do. This is why the recorder exists | Android and Apple docs |
| Upload fails | A finished run, "waiting to upload" | The run on the phone | Retry later, resume from the last good chunk | My design |
| Battery almost empty | A saved run and a message | The database | Stop cleanly under a set level | My design |
Releases:
- A recording bug ships to everyone, and a lost run cannot be re-run. A staged rollout can be halted, but installed copies keep the version they have, as in the chat app case.
- The filter numbers (accuracy limit, jump limit, minimum move) can come from a remote flag, and recomputing a run from raw points means a better filter fixes old runs too.
- I would watch: runs that end as "interrupted", runs with a gap, the share of recordings where the first point takes over 30 seconds, upload failures, and battery use per hour of recording.
Say this. "The phone is the first copy and the server the second. A run survives a kill, a revoked permission and a bad upload, and the user always sees what happened. I left out sharing, route planning and the watch."
6. Follow-ups that change a constraint
In short: Each follow-up changes one constraint. A strong answer says what breaks.
Practice prompts, not a claim about what any interviewer asks.
- Make it a 10 hour hike. Strong: lower the rate and spread points by distance, batch more, cap the notification work, and check the file size: 10 hours at one point every 5 seconds is 7,200 points, about 288 KB at 40 bytes. Weak: keep one fix a second for 10 hours.
- Add live sharing. Strong: a second path that sends the latest point when the network exists, while the local database stays the first copy. Weak: send every point and drop it if the network fails.
- Add auto-pause. Strong: detect "not moving" from the filtered speed over a short window, and pause the clock and not the recorder. Weak: pause when one fix looks still.
- Add a smartwatch. Strong: the watch can be the recorder, and the phone merges two sources by time. Weak: run both and add the distances.
- The user says the distance is wrong. Strong: raw points are on disk, so I can replay the run and show the dropped points. Weak: "GPS is not exact."
- Two phones, one account. Strong: runs are keyed by their own recording id, so both appear once in the list. Weak: a shared "current run" on the server.
7. What changes by level
In short: As I read it, the levels differ in who raises the problem: the interviewer, the candidate, or the candidate with a rollout plan.
These levels are my own reading of public prep material, not any company's rubric.
- Mid-level: Start and stop, a service for the background, a local save and an upload. I want to hear: "The screen cannot own the recording."
- Senior: A state machine on disk, batched writes, recovery after a kill with a gap, permission cases, and filtered distance. I want to hear: "The database is the memory of the run, so a kill costs seconds, not the run."
- Staff: Raw points kept so the maths can improve, filters behind a flag, battery and interruption metrics, the cost of the background permission, and a plan for watch and live sharing. I want to hear: "Here is what I would watch, and here is the flag that changes the filter."
Common weak answers. These repeat across public prep material. They are not a dataset.
- Keeping the route in memory until Stop.
- The screen owning the location updates.
- Asking for "Allow all the time" without a reason.
- Adding up raw distances with no filter.
- No plan for a kill or a permission change.
Test yourself
In short: Six questions from this case. Answer each one out loud first, then open the answer.
1. Why can the recording screen not own the location updates?
When the screen locks, the app goes to the background. A background app on Android gets location updates only a few times each hour, and on iOS the system suspends most apps shortly after they move to the background. A recorder (a foreground service, or a background activity session) keeps the stream alive and the screen only reads the saved points.
2. Why save points to a database in small batches, and not keep a list and save at Stop?
The system can kill the app at any time. Points kept only in memory are lost with it. A batch every few seconds in one transaction means a kill costs at most the last batch, and writes stay cheap on the battery.
3. Does this app need the Always (background) location permission?
As I read the docs, no. The user taps Start while the app is open, and a foreground service or background activity session carries on from there. Android says a location foreground service cannot be started from the background without the background permission, so the app starts it only from the visible app.
4. The app was killed at minute 50. What does the next start do?
It looks for a recording that still says RECORDING while nothing is running. It shows the run so far and offers Resume or Finish. A resume starts a new segment, so the gap is marked, never drawn as a line and never counted as distance.
5. Why store raw points and work out distance later?
The distance maths and the filter numbers will be wrong at first. With raw points on disk, I can change them and recompute every old run. If only a running total was saved, a bug would stay in every run.
6. Why can a 10 km run read as 13 km, and what do you do?
At one point a second, points are only about 3.3 m apart, so small errors add up. If each of 3,000 segments gains 1 m of noise, that is 3,000 m extra. Drop low-accuracy fixes and impossible jumps, and add distance only for a move bigger than the noise.
Further reading (official docs)
- Design a state machine for a mobile flow: the states and the lost-answer path
- Design photo and file sync: uploads that resume and survive a kill
- Guide: idempotency keys
- Android: location permissions
- Android: foreground service types
- Android: background location limits
- Apple: handling location updates in the background
Words on this page (5)
- Foreground service
- On Android, work that keeps running with a visible notification, so the system is less likely to stop it.
- Fix
- One location reading from the phone: a place, a time and an accuracy.
- Accuracy
- How far off a fix may be. A fix of 50 metres accuracy could be anywhere in a circle of that size.
- Segment
- A part of a recording with no gap. A pause or a kill starts a new one.
- Transaction
- A group of database writes that all happen or none happen.
Verification notes (last reviewed 16 Oct 2026)
My own opinion or design.
- The section order, decision beats, spoken scripts, failure table cells, strong and weak notes and level text are new wording and my opinion.
- The two local tables, the segment and gap design, the 10 second batch, and the filter steps and numbers are my design.
- Starting the recorder only from the visible app, to avoid the background permission, is my reading of the docs below. Test it on real devices.
- The follow-up questions are my reconstruction of challenges that public prep material reports, not transcripts of real rounds.
- The level text is my reading, not a company rubric. The 45 minutes is the interview length, not a documented fact.
Fetched and read on 2026-10-16. I wrote down what each page says.
- Android, location permissions:
- Foreground access is when an activity is visible or the app runs a foreground service. Background access is anything else.
- A foreground service that uses location must declare the location type on Android 10 and higher.
- To request background access at runtime on Android 10 and higher, the manifest must declare ACCESS_BACKGROUND_LOCATION.
- Approximate location is about 3 square kilometres. Precise is usually about 50 metres, sometimes a few metres.
- Android 12: if the power saver mode is LOCATION_MODE_FOREGROUND_ONLY, an app running a foreground service keeps getting updates with the screen off.
- Android, foreground service types:
- The location type needs FOREGROUND_SERVICE_LOCATION, the base FOREGROUND_SERVICE permission, and location services on with at least one of the coarse or fine permissions granted.
- You cannot create a location foreground service while the app is in the background, unless it has ACCESS_BACKGROUND_LOCATION.
- Android, background location limits:
- On Android 8 and higher, a background app gets location updates only a few times each hour, whatever it asks for.
- Foreground apps keep the older, faster rates.
- On Android 11 and higher, a foreground service started from the background cannot use location without the background permission.
- Apple, handling location updates in the background:
- The system suspends most apps shortly after they move to the background. A suspended app gets no location updates, and the system queues them.
- Tracking a hike or workout path is listed as a use for background updates.
- Enable the Location updates background mode, and create a CLBackgroundActivitySession. Create a CLServiceSession while in the foreground, and recreate it right away if the app is relaunched in the background.
- The system can terminate apps at any time. On relaunch, restart the location APIs and the system resumes delivery of queued updates.
Not verified.
- The Android location request options (priority, interval, minimum distance, batching). I used them only as a general idea.
- Whether Android restarts a location service after the system kills it.
- Apple's own CLBackgroundActivitySession page did not load. I used the background article instead.
- How phone makers add their own battery limits on top of Android.
- The numbers are my assumptions. I recomputed: 3,600 x 40 = 144,000 bytes; 3 x 3,600 = 10,800 points and 10,800 x 40 = 432,000 bytes; 3,600 / 10 = 360; 100,000 x 144 KB = 14,400,000 KB; 3,000 x 1 m = 3,000 m; 10 hours x 3,600 / 5 = 7,200 points and 7,200 x 40 = 288,000 bytes; a 5 min/km pace is 1,000 m in 300 s, which is 3.3 m a second.
Changes by review pass.
- Source-check pass, 2026-10-16: read the pages above and wrote the claims from them.
- Numbers pass, 2026-10-16: recomputed every figure in the tables and the 13 km example.
Keep reading
Get the free one-page cheat sheet
One page: the five steps, a 45-minute time plan and two checklists. Print it or keep it on your phone.
We email you the cheat sheet after you confirm your address. You can also open it now from the Start here page. You get a short note when a new case is published. Unsubscribe any time. Privacy.