“Design Gmail mobile” sounds boring until you start. Then you realize: 1B+ users, threads with 100+ messages, multi-megabyte attachments, search across years of mail, push that wakes the app within seconds of arrival, offline composition, and labels/folders that sync across devices. The interview tests whether you understand layered sync, search index design, and conflict resolution at scale.
Functional requirements
- List inbox and folders/labels
- Read messages with threading
- Compose, reply, forward
- Search with sub-second results
- Push notifications on new mail
- Offline access to recent mail
- Multi-account support
Architecture
Three modules: local store, sync engine, push integration.
Local store
SQLite. Tables for messages, threads, labels, attachments, drafts. FTS5 for full-text search. The app caches recent messages locally; older messages are server-only.
Storage budget: typically last 30 days of mail, plus all starred or labeled messages, plus full thread for any thread with a recent message.
Sync
Gmail uses a History ID model. Each mailbox has a monotonically increasing history ID. Client tracks last-seen ID; on sync, requests “all changes since X.” Server returns adds, modifications, deletions.
Two-way sync: client-side actions (mark as read, archive) generate operations queued for upload. On reconnect, ops are applied to the server, history advances, and sync continues.
Push notifications
Server pushes a “data available” notification on new mail. Client wakes briefly, fetches the new message, and renders the notification with subject and snippet. Privacy mode: notification body is generic, content is fetched only when app opens.
Search
Two layers:
- Local FTS: recent messages, instant results
- Server search: full mailbox, slower (200–1000ms), broader scope
UI shows local results first, then merges server results when they arrive.
Offline composition
Drafts saved to local SQLite with status pending. On send tap, the message moves to outbox. Background uploader (URLSession or WorkManager) attempts delivery; on success, transitions to sent.
Attachment uploads happen in chunks. Resumable across app restarts.
Threading
Threads are server-computed by Gmail (RFC 822 headers + Subject + In-Reply-To). The client just renders the pre-grouped thread.
Multi-account
Each account has its own sync state and local DB tables. Common code path; separate credentials. Switching accounts is instant because everything is local.
Battery and data
- Sync is push-driven; no polling
- Attachments downloaded on tap, not eagerly
- Background sync is rate-limited by OS budget
Frequently Asked Questions
How does Gmail handle a deleted email that arrives via push?
Push tells the client “history changed.” Client fetches the change, sees a delete, removes the message locally. The notification is suppressed.
How does threading handle replies from non-Gmail clients?
RFC 822 References and In-Reply-To headers. Most modern clients respect these; older ones do not, and threading falls back to subject + sender heuristics.
Why does email search sometimes miss recent messages?
Local FTS indexing happens asynchronously. Very recent messages (last few seconds) may not yet be in the index.