System Design & Architecture
System design questions test a developer's ability to abstract a massive feature or app into scalable, robust technical components.
General Approach​
When given an Android system design prompt (e.g., "Design the Twitter Feed" or "Design a Ride-Sharing Map App"):
- Clarify Requirements: Ask questions. Is offline support required? Are we dealing with real-time sockets or polling? Image uploads?
- High-Level Diagram: Propose standard MVVM + Clean Architecture flow. Note where Repositories sit and how UseCases abstract logic.
- Data Layer Strategy: Discuss API interactions (REST vs GraphQL/WebSockets) and local persistence (Room). Explain the caching strategy (Single Source of Truth paradigm).
- Offline support/Sync (crucial): How does the app handle a dropped connection? (E.g., Save API actions to Room, push to server via WorkManager when connection returns).
- Pagination: How are massive lists rendered without memory limits blowing up? (Paging 3 + RemoteMediator).
- Performance & Security: Image Caching (Coil/Glide), Certificate Pinning, Obfuscation (R8), Encryption (EncryptedSharedPreferences).
Scenario A: Design an Offline-First Chat Application​
Requirements:
- Send/Receive distinct text messages.
- Must work robustly without internet connection (queueing).
Architecture Components:
- Network: Use WebSockets or Server-Sent Events (SSE) for real-time delivery, not REST polling. Fallback to FCM (Firebase Cloud Messaging) pushing wake locks if the app is killed.
- Local Database: Room Database. Specifically, a
Messagetable with async_statuscolumn (PENDING,SENT,FAILED). - Single Source of Truth:
- The UI absolutely NEVER observes the network directly.
- The UI only observes a Flow from the Room DAO (
SELECT * FROM messages WHERE chat_id = X ORDER BY timestamp ASC). - Network responses write strictly to Room.
- Offline Queueing:
- App is offline. User types "Hello".
- ViewModel saves "Hello" to Room with
sync_status = PENDING. The UI instantly shows it (optimistic update). - Enqueue a
WorkManagertask or start a job usingConnectivityManagercallbacks. - When the internet restores,
WorkManagerintercepts the payload, posts to the WebSocket, updates the Room column toSENTupon success.
- Pagination: Paging 3 loads older messages from Room natively as the user scrolls up.
Scenario B: Design an Instagram Image Feed​
Requirements:
- Extremely fast image loading.
- Smooth scrolling without UI Jank.
- Pagination.
Architecture Components:
- Data loading: REST API endpoint using offset/cursor pagination. Paging 3
RemoteMediatorsits between the raw API DTO mapper and the Room Cache layout. - Image Loading Engine (Glide/Coil):
- Memory Cache: L1 Cache. A HashMap maintaining decoded Bitmaps based on LRU (Least Recently Used) policies to render instantly on scroll.
- Disk Cache: L2 Cache. Encoded JPEGs/WebP saved to app scope cache directory so repeated app opens don't require network transfers.
- Downsampling: The raw backend image might be 4000x4000 pixels. The Image Pipeline must detect the ImageView boundaries (e.g., 500x500) and decode the Bitmap scaled to exactly that size to dodge
OutOfMemoryErrorlimits.
- RecyclerView / LazyColumn:
- Strict usage of
DiffUtilso appending page 2 doesn't stutter the visible page 1 items. - Pre-fetching URLs using RecyclerView's
PreloadItemmechanism so the next image downloads before the user scrolls to it.
- Strict usage of
Scenario C: Massive Legacy Refactoring​
Requirements:
- Taking over a large Monolithic Java App (God Activities, MVC).
- Transitioning it to Kotlin, Compose, MVVM, and Modularized architecture.
Strategy:
- Stop the Bleeding: Create a boundary. All new features are built strictly in Kotlin using MVVM/Compose in separate modules.
- Modularization first: Rip out generic utility functions (Date parsing, pure Network clients) into
core:utilsandcore:networkmodules. - Strangler Fig Pattern: Use Jetpack Navigation. Slowly extract single
Activityscreens intoFragment/Composedestinations hosted in a modernMainActivity. - Unit Tests are mandatory: Before touching complex legacy God classes in Java, write heavy JUnit characterization tests tracking inputs and outputs perfectly to ensure refactoring logic into Kotlin UseCases doesn't cause hidden regressions.