Irbisa · Cheatsheet
Mobile System Design
Offline-first, sync, pagination, caching, modularization, feature toggles
In a mobile system design round, how do you structure the 45 minutes so you cover what the interviewer is grading?
You like a post on the detail screen, go back, and the feed still shows it unliked. What is wrong with the architecture?
// Wrong: each screen owns a copy, so a write on one is invisible to the otherclass DetailViewModel(private val api: Api) : ViewModel() { val post = MutableStateFlow<Post?>(null) fun like(id: String) = viewModelScope.launch { post.update { it?.copy(liked = true) } // only this screen will ever know…How do you design search-as-you-type so it stays responsive without hammering the backend?
private val query = MutableStateFlow("") val results: StateFlow<SearchUi> = query .map { it.trim() } .distinctUntilChanged() .debounce(250)…Product wants every screen fresh, and the app still fires forty requests on cold start. How do you decide what to cache and for how long?
// One policy per resource class, not one number for the whole appenum class Freshness(val ttl: Duration) { NEAR_STATIC(24.hours), // serve from cache within the ttl USER_OWNED(5.minutes), // serve from cache within the ttl VOLATILE(Duration.ZERO) // never render silently from cache}…Opening the profile screen fires seven requests and it stays blank until the slowest one returns. What do you change, on both sides?
// Wrong: awaitAll means the screen is blank until the slowest call finishesval state = coroutineScope { val header = async { api.header(id) } val stats = async { api.stats(id) } val recs = async { api.recommendations(id) } // 900 ms p95, blocks the name ProfileUi(header.await(), stats.await(), recs.await())…iOS shows one order total, Android shows another, and the emailed receipt shows a third. What went wrong in the design?
A limited offer must end at midnight, but some users see it for another day. Which clock can the app trust?
// Wrong: the countdown and the gate both trust a clock the user can changeval remaining = offer.endsAtMillis - System.currentTimeMillis() // Right: one anchor, refreshed on every response, used wherever "now" is neededclass ServerTime { private data class Anchor(val serverMs: Long, val elapsedMs: Long)…Six months after a release, people are still opening that old build. What does that force you to design differently?
How would you design the feed screen of an Instagram-like app so scrolling feels continuous and survives rotation and process death?
class FeedViewModel(private val repo: FeedRepository) : ViewModel() { val items: StateFlow<List<FeedItem>> = repo.observeFeed() // rows from the local DB .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) private var nextCursor: String? = null // persist it in a real app private var endReached = false…How do you run feature flags and remote config in a large app without ending up with hundreds of stale branches?
// One registry: every flag has an owner and a removal date, so deletion is scheduled workenum class Flag(val key: String, val owner: String, val removeBy: LocalDate?) { CHECKOUT_V2("checkout_v2", owner = "payments", removeBy = LocalDate.parse("2026-12-01")), NEW_SEARCH("new_search", owner = "discovery", removeBy = LocalDate.parse("2027-01-15")), PAYMENTS_ENABLED("payments_enabled", owner = "payments", removeBy = null), // permanent kill switch}…Design an in-app cache with memory and disk layers: how do you implement LRU eviction, size limits and thread safety?
class LruMemoryCache<K : Any, V : Any>( private val maxBytes: Long, private val sizeOf: (V) -> Long,) { // accessOrder = true: every get() moves the entry to the tail (most recently used) private val map = LinkedHashMap<K, V>(16, 0.75f, true)…How would you design an offline-first app: local source of truth, sync on an unreliable network, and conflict handling?
class NotesRepository( private val db: AppDatabase, private val work: WorkManager,) { fun observe(): Flow<List<Note>> = db.notes().observeAll() // the UI reads only this…How would you design a chat app like WhatsApp: transport, delivery states, the offline queue and message history?
suspend fun send(chatId: String, text: String) { val clientId = UUID.randomUUID().toString() db.withTransaction { messages.insert( MessageEntity(clientId = clientId, chatId = chatId, text = text, serverSeq = null, state = State.SENDING) // shown at once, at the bottom…How would you design an image-loading library like Coil or Glide?
class ImageLoader( private val memory: LruCache<String, Bitmap>, // sizeOf() returns byteCount private val disk: DiskCache, private val fetcher: Fetcher, private val decodeDispatcher: CoroutineDispatcher = Dispatchers.Default.limitedParallelism(2),) {…Design a vertical short-video feed like TikTok or Reels: prefetching, a pool of players, caching segments, and starting playback instantly on swipe.
@OptIn(UnstableApi::class)class ShortsPlayback(context: Context, cache: Cache, clips: List<Clip>) { private var current = 0 private val builder = DefaultPreloadManager.Builder(context) { index: Int -> when (abs(index - current)) { 1 -> PreloadStatus.specifiedRangeLoaded(2_000L) // neighbours: 2 s in memory…Design a download and upload manager for large files: queueing, retries, resume and progress that survive the app being killed.
class DownloadWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) { override suspend fun doWork(): Result { val job = dao.get(inputData.getString("id")!!) ?: return Result.failure() val part = File(job.targetPath + ".part") val done = if (part.exists()) part.length() else 0L…Design an analytics and logging SDK for the app: batching, an offline buffer, sampling and swapping vendors.
interface AnalyticsSink { suspend fun send(batch: List<StoredEvent>) } // vendor adapters class Analytics( private val queue: EventDao, private val scope: CoroutineScope, private val sampler: Sampler,…When is server-driven UI worth it, and what goes wrong when a team takes it too far?
@Serializablesealed interface Block { @Serializable @SerialName("hero") data class Hero(val imageUrl: String, val title: String, val action: String) : Block @Serializable @SerialName("product_row")…How would you design the client of a ride-hailing or nearby-friends app that shows live locations without draining the battery?
How would you structure a super-app where a dozen teams ship features into one Android app?