Mobile system design · 17 min
Mobile system design interview: how the round is scored
How a mobile system design round runs, the seven things a senior answer covers, a worked offline sync example, and how Senior and Staff are scored.
Mobile system design · 15 min read
What an Android design round adds: process death, WorkManager, Doze, FCM priority and Room as source of truth, with a worked chat design and its follow-ups.
An Android system design round uses the same skeleton as any mobile design round (see Mobile system design interview), and then tests whether the design survives Android. The process can be killed whenever it is in the background. Background work is deferred by Doze and rationed by standby buckets. A push message can arrive while the app is not running. An answer that ignores those facts reads as a web design with a phone drawn around it.
| Area | The common wrong answer | What Android does |
|---|---|---|
| State | “The ViewModel holds it” | ViewModels do not survive process death |
| Background work | “A background thread syncs every 10 minutes” | Work is deferred in Doze; standby buckets limit how often an app runs |
| Long transfers | “Start a service” | Background-started foreground services are restricted; services need a declared type |
| Push | “FCM delivers the message instantly” | High priority messages are for user-visible events and can be deprioritised if misused |
| Storage | “SharedPreferences for everything” | Room for records, DataStore for preferences, Keystore-backed keys for secrets |
Process death is normal. Android kills background processes to reclaim memory. When the user returns, the system restores the back stack and saved instance state, not your in-memory objects. Anything the user would be upset to lose is written to disk as it changes.
WorkManager is the default for deferrable, guaranteed work. It persists work across process death and reboots, respects constraints (network, charging), and applies backoff. It does not run work at an exact time. For “sync when the network comes back”, it is the right answer.
Doze and App Standby Buckets. On a stationary device with the screen off, Doze batches network access and jobs into maintenance windows. Standby buckets (active, working set, frequent, rare, restricted) set how often an app’s jobs may run, based on how recently the user used it. A design that assumes regular background runs must say what happens to a user in the rare bucket.
Foreground services are for work the user is aware of, with a notification, and must declare a type (required since Android 14). Starting one from the background is restricted. For a user-initiated large upload with visible progress, a foreground service or a long-running worker in WorkManager is appropriate; for silent sync, it is not.
FCM message priority. High priority messages can wake the device from Doze and are meant for time-sensitive, user-visible events such as a chat message. Using high priority for messages that do not result in a notification can get them deprioritised. Normal priority messages may wait for a maintenance window.
These are Android system design interview questions as they are usually posed, with the platform detail each one is really testing. The product is the wrapper; the Android behaviour underneath is what gets scored.
| Prompt | The Android detail it tests | The answer that scores |
|---|---|---|
| “Design a chat app with offline send.” | Process death and guaranteed delivery | Messages written to Room before they show as sent; WorkManager sends them when the network returns |
| “Design a photo or file upload.” | Work the user can see versus work they cannot | A user-initiated upload with progress runs as a long-running worker with a notification; silent sync stays deferrable |
| “Design a news feed that works offline.” | Room as the source of truth | The UI observes the database; the network only updates it, so the feed survives the process being killed |
| “Design notifications for this app.” | FCM priority and Doze | High priority only for user-visible events; normal priority waits for a maintenance window |
| “Design background sync for a to-do app.” | Standby buckets and constraints | WorkManager with a network constraint, and a design that is correct for a user in the rare bucket |
| “Design a long form the user fills in over several minutes.” | Saved state and process death | Input persisted as it changes; the screen restores from saved state and disk, not from the ViewModel alone |
Each of these is answered with the platform facts above. The detail that decides many Android rounds is process death: say it before the interviewer does, and show where each piece of state survives it.
Prompt: “Design the Android client for a one-to-one chat app. Messages must be sendable offline.”
One-to-one only, text first, images later. Messages appear on the other device within seconds when both are online. Message history syncs across the user’s devices. Scope out: group chat, end-to-end encryption (mention it as a later decision that changes the server’s role).
sending, in one transaction. A SyncWorker is enqueued with a network constraint, as unique work so repeated sends do not create parallel workers.sent with the server timestamp, and retries with exponential backoff on failure.WorkManager guarantees that the worker runs again. It does not guarantee that the worker remembers where it stopped, and the process can die in the middle of doWork(). So progress lives in Room, never in the worker’s memory. And not every failure is retried. Sort each response into four classes: retryable (transport errors, 503, 429: back off and resend under the same client id), terminal (the server rejected the message: mark it failed and show it), unknown (the request may have landed: resend with the same id, never a new one), and user-actionable (the session needs a new sign-in: stop and ask). A worker that backs off on everything retries a rejected message forever.
The user types a long message, switches to another app, and the process dies. The draft survives only if it was saved: a draft column in Room updated as the user types (debounced), or SavedStateHandle for short text. If you debounce, say how many keystrokes are unsaved in the gap. Say how you would test it: “Don’t keep activities” only destroys activities, so it is not process death. Background the app and run adb shell am kill <package>, then reopen it. Force-stop is a third case, and it clears pending work.
Time from send to server acknowledgement, count of messages stuck in sending for more than a minute, WorkManager failure counts by reason, notification delivery versus open rates, and crash and ANR rates from Play vitals.
| Follow-up | What it tests | A strong answer |
|---|---|---|
| “The phone is in Doze for hours.” | Knowing the platform limits | High priority FCM for visible messages; history catches up on the next window or when opened |
| “Two messages sent offline arrive out of order.” | Ordering design | Client ids, server sequence after ack, local order only for pending |
| “The same message is sent twice after a retry.” | Idempotency | The server deduplicates by client id |
| “The server rejects one message for good.” | Failure classification | Terminal outcomes stop the retries; the message shows as failed with a reason |
| “Add image messages.” | Large transfers | Store the file locally, upload in a worker with progress, send the message once the upload has an id |
| “The user reinstalls the app.” | Source of truth | The server holds history; the local database is a cache that rebuilds |
| “The process dies after the server accepts a message, before the app records the answer.” | The lost response | Resend under the same client id and let the server return the existing message. Without server deduplication, no amount of backoff fixes it |
| “The sync cursor has expired.” | History recovery | Resync from the server and keep the pending local messages instead of replacing the database |
| “The user signs out with messages pending.” | Account ownership | Pending messages belong to the account that wrote them; the next account never sends them with its own credentials |
A Senior answer designs this well. A Staff answer adds the parts beyond the client: the API contract (client ids, sequence numbers, sync cursors) agreed with backend, a sync module other features can reuse, the migration plan for existing users’ local data, and a rollout with a remote flag on the new sync path plus the metric that would trigger turning it off. How the Staff call is made in the debrief is in the Staff mobile system design interview.

Mobile system design · 17 min
How a mobile system design round runs, the seven things a senior answer covers, a worked offline sync example, and how Senior and Staff are scored.

Android · 16 min
Twelve senior Android interview questions with what each scores, a weak and a senior answer, and the follow-up: coroutines, Flow, Compose, process death.

Android · 14 min
Ten Android architecture interview questions and how senior answers are scored: layers, the domain layer, UDF, Hilt, modules, navigation and testing.