Senior Interview PrepProduction Patterns2025 Best Practices

Android Engineering Playbook

Architecture patterns, engineering trade-offs, and production-grade techniques every Android engineer should have in their toolkit.

10 Core Topics
Code Examples
Visual Diagrams
Interview Tips
0/ 10 studied
01

Architecture Patterns

Critical~8 min

The foundation of every scalable Android app. Clean Architecture with MVVM or MVI ensures strict separation of concerns, independent testability of each layer, and maintainability as the codebase grows.

Clean Architecture Layers

Presentation Layer
Activity / ComposableViewModelUI State
depends on
Domain Layer
UseCaseRepository InterfaceDomain Model
depends on
Data Layer
Repository ImplRoom DAORetrofit Service

Key Considerations

Dependency rule: outer layers depend inward — Presentation → Domain ← Data. Never reverse this.
Domain layer must have zero Android imports — pure Kotlin for maximum testability and portability.
Use MVVM for straightforward screens; switch to MVI when state complexity grows (multiple concurrent events).
Repository pattern abstracts data sources — UI never knows or cares if data comes from DB or network.
MVVM
StateFlow/LiveData
Two-way binding
Simpler state
MVI
Sealed Intent class
Immutable State
Side Effects

Interview Tip: When asked "how do you structure a large Android app?", name the three layers explicitly and emphasize that the Domain layer has NO Android imports — this single detail signals deep architectural understanding. Also mention that UseCases encapsulate one business operation each, making them trivially testable.

02

Offline-First & Caching

Critical~7 min

Users expect apps to function without internet. Single Source of Truth (SSOT) with Room as the local DB and Retrofit for remote data is the industry gold standard. The key insight: UI observes Room, network just populates it.

Offline-First Data Flow

UI / ViewModel
Repository — Single Source of Truth
Cache-First → refresh → emit Flow
Room DB
Local — Offline
Retrofit API
Remote — Network

Caching Strategies

Cache-FirstReturn cache immediately, refresh in background. Best UX.
Network-FirstTry network, fallback to cache on failure. Best freshness.
Stale-While-RevalidateServe stale cache + silently refresh. Balanced approach.
Cache-OnlyAlways serve cache. For static content (countries, config).
Network-OnlyNo cache. For real-time or security-sensitive data.

Interview Tip: The critical insight: Room returns a Flow — so any Room update automatically re-emits to all collectors. This means you never need to manually refresh the UI. The network layer just "writes to Room" and the UI updates itself. This is the reactive SSOT pattern.

03

Modular Architecture

High~6 min

At scale, a monolithic app becomes a liability: slow builds, merge conflicts, inability to parallelise development. Feature modules enable team scalability, faster incremental builds, and Play Feature Delivery for smaller APK size.

Module Dependency Graph

:app
:feature:home
:feature:profile
:feature:checkout
:core:ui
:core:data
:core:network
:core:domain — Zero Android deps, pure Kotlin
Feature modules can only depend on :core, never on each other — prevents circular dependencies.
Dynamic Feature Modules reduce initial APK size by 30–60% via on-demand download.

Module Types & Responsibilities

:appOrchestrator, DI root, Navigation graph
:feature:*Self-contained screens + ViewModels
:core:networkRetrofit, OkHttp, interceptors
:core:dataRoom, DataStore, repositories
:core:uiShared composables, Design System, Theme
:core:domainUseCases, interfaces, domain models

Interview Tip: Mention the build time impact: in a 50-engineer team, modularisation reduces Gradle compile time from 10+ minutes to under 2 minutes because only changed modules recompile. Also mention convention plugins (build-logic module) to avoid duplicating Gradle config across 20+ modules — interviewers at large companies love this detail.

04

State Management

Critical~8 min

Predictable, unidirectional state is the foundation of bug-free UIs. Understanding the distinction between screen state (StateFlow), one-time effects (SharedFlow/Channel), and Compose state hoisting is essential for senior engineers.

Unidirectional Data Flow (MVI)

User Action
Intent
ViewModel
Process
New State
State
UI Render
View
↺ loop

StateFlow vs SharedFlow vs Channel

StateFlowScreen state
UI screen state — always has a value, replays last to new collectors
SharedFlowSide effects
One-time events — navigation, snackbar. No sticky value.
ChannelAnalytics
Fire-and-forget events with backpressure handling

Compose State Hoisting

Hoist state to the lowest common ancestor that needs to read or modify it.
Stateless composables are more reusable, previewable, and easier to test.
Use rememberSaveable for state that must survive process death (form inputs, scroll position).
Always use collectAsStateWithLifecycle() — avoids background collection that wastes battery.
State hoisting pattern
State flows up (lifted to parent) · Events flow down (callbacks to child)

Interview Tip: The most common interview mistake: using StateFlow for navigation events. On screen rotation, a new collector subscribes and immediately receives the last emitted navigation event — causing double navigation. Always use Channel or SharedFlow(replay=0) for one-time effects. This gotcha shows you have real production experience.

05

Networking & API Design

Critical~9 min

Robust networking handles unreliable mobile connections gracefully. Key concerns: layered interceptors for cross-cutting concerns, retry with exponential backoff, token refresh race conditions, and choosing REST vs GraphQL for your data shape.

Network Layer Stack

Repository
Single entry point
OkHttp + Interceptors
Auth, Retry, Logging
Retrofit Interface
Type-safe calls
Moshi / KotlinX
JSON ↔ Model
Server (REST/GraphQL)
External API
● request →● ← response

REST vs GraphQL vs gRPC

REST
HTTP-cacheable, simple, widely supported
Over/under-fetching data
GraphQL
Precise data shape, single endpoint, no versioning
No HTTP caching, complex client
gRPC / Protobuf
Binary protocol, streaming, very fast
Limited browser support, complex setup

Interview Tip: The token refresh race condition is a classic senior interview question. When 5 requests fail with 401 simultaneously, naive implementations refresh the token 5 times. The Mutex solution ensures only one refresh happens and all others wait for the result — this shows you understand concurrency in production networking.

06

Background Processing

High~7 min

Android aggressively kills background processes to preserve battery. WorkManager is the modern standard for guaranteed deferrable work. Understanding which tool to use — WorkManager, Coroutines, or Foreground Services — is a key decision point.

WorkManager Chain

CompressWorker
UploadWorker
NotifyWorker
Chain via .then() — sequential, guaranteed, survives process death
PeriodicWork
OneTimeWork
ExpeditedWork

Right Tool for the Job

WorkManagerSync, photo upload, DB cleanup
Guaranteed background work, survives reboot/process death
Coroutines + FlowNetwork calls, DB queries, computation
Async operations within app lifecycle
Foreground ServiceMusic playback, GPS tracking, calls
Long-running user-visible background task
AlarmManagerCalendar reminders, scheduled alerts
Exact-time triggers, even if app is dead

Interview Tip: When asked "WorkManager vs JobScheduler?": WorkManager wraps JobScheduler (API 23+) and adds guaranteed execution across process death, chainable work, unique work (prevent duplicate jobs), observability via Flow/LiveData, and Hilt injection. You should never use JobScheduler directly in new code.

07

Performance & Memory

High~7 min

Memory leaks and frame drops are silent killers in production. Understanding the rendering pipeline, common leak patterns, Baseline Profiles, and measurement tooling is what separates senior engineers from mid-level ones.

Common Memory Leak Sources

Activity / Fragment — blocked from GC
Static Context Ref
Anonymous Listener
Handler w/ Activity
Singleton w/ Context
Fix: viewModelScope, lifecycle-aware observers, WeakReference

Optimisation Toolkit

Baseline ProfilesPre-compile critical paths; reduces cold start by up to 40%
App Startup LibraryOptimise ContentProvider init order, lazy initialisation
R8 Full ModeDead code removal + obfuscation + optimisation in one pass
Lazy DI (Hilt)Lazy<T> for heavy deps that aren't always needed
RecyclerView DiffUtilPartial list updates instead of full notifyDataSetChanged()
Coil/Glide BitmapPoolReuse Bitmap memory instead of allocating/GCing repeatedly
StrictMode (Debug)Catch disk/network on main thread during development

Interview Tip: Mention Android Vitals metrics: cold start < 5s, ANR rate < 0.47%, crash rate < 1.09% to stay in Play Store good standing. Baseline Profiles are compiled by the Play Store before first launch — this is how Google's own apps achieve sub-1s cold starts. Bring up Macrobenchmark for measuring in CI.

08

Security

Critical~8 min

Defence in depth for mobile: secure the network layer, encrypt local storage, obfuscate code, and add runtime protections. Non-negotiable for fintech, healthcare, and any app handling user credentials or PII.

Security Layers

App Layer
ProGuard / R8
Root Detection
Network Layer
SSL Pinning
Cert Transparency
Storage Layer
EncryptedSharedPrefs
Android Keystore
Runtime Layer
Biometric Auth
Screenshot Block

Key Considerations

SSL Pinning prevents MITM attacks. Pin the CA public key (not leaf cert) to survive certificate rotation without an app update.
Android Keystore stores private keys in hardware-backed TEE. Keys are non-exportable and can require biometric auth before use.
EncryptedSharedPreferences uses AES-256-GCM. Use for tokens and sensitive flags; not suitable for large binary data.
Play Integrity API replaces SafetyNet for device attestation — verifies app integrity and device certification level.
Never log sensitive data. Gate all logging behind BuildConfig.DEBUG and strip with ProGuard in release builds.

Interview Tip: For fintech/healthcare interviews, describe the full threat model: network (SSL pinning + HSTS), storage (Keystore + EncryptedSharedPrefs), runtime (root detection + Play Integrity API), code (R8/ProGuard obfuscation + string encryption). Mentioning backup pins to avoid outages on certificate rotation is a detail that stands out.

09

Real-Time & Push Notifications

High~6 min

Choosing the right real-time strategy has profound implications for battery life, server infrastructure costs, and user experience. Each protocol has a different latency, battery, and complexity trade-off.

WebSocket — Full-Duplex Live

Android Client
connected
WebSocket — full-duplex
Server
listening

Protocol Comparison

ProtocolLatency ↓ betterBattery ↓ betterBest For
WebSocket
FULL-DUPLEX
~5ms
High
Chat, Gaming
complexity
SSE
SERVER→CLIENT
~100ms
Medium
Live feeds
complexity
Long Polling
LEGACY
~500ms
Very High
Legacy apps
complexity
FCM Push
RECOMMENDED
~1s
Low
Notifications
complexity
Short Polling
AVOID
1–30s
Extreme
Avoid it
complexity
best
moderate
worst

Interview Tip: On FCM: explain the critical difference between notification messages (system-handled, shown automatically) vs data messages (your app handles, full control over UI). For chat apps, always use data messages so your app can decrypt, format, and show the notification — notification messages expose plaintext to the system.

10

Testing Strategy

High~7 min

A robust test strategy catches regressions before users do. Follow the testing pyramid: many fast unit tests, fewer integration tests, minimal but high-value E2E tests. Clean Architecture makes each layer trivially testable in isolation.

Testing Pyramid

E2E / UI Tests (Espresso)~10%
Slow · Expensive · Fragile
Integration Tests (Hilt)~20%
Moderate speed · Real DB
Unit Tests (JUnit5 + MockK)~70%
Fast · Isolated · Many
▲ Fewer tests, more coverage | ▼ More tests, more isolation

Essential Libraries

UnitJUnit5 + MockKUnit tests with expressive Kotlin mocking
UnitTurbineTest Flow/StateFlow emissions step by step
UnitKotlinx Coroutines TestUnconfinedTestDispatcher for ViewModel tests
IntegrationHilt TestingReplace production modules with test fakes
IntegrationRoom in-memory DBTest DAO queries with real SQLite logic
UIEspresso + UiAutomatorInstrumented UI tests on emulator/device
UICompose UI TestingSemantic node finders for Compose screens
PerfMacrobenchmarkMeasure cold start, scroll jank in CI

Interview Tip: Show you understand the test double hierarchy: use fakes (real in-memory implementations) for repositories to avoid fragile mock setups, mocks (MockK) only when you need to verify interactions were called, and stubs for simple return values. Over-mocking leads to tests that pass when the code is broken.

You are now interview-ready.

System design is a conversation about trade-offs, not a quiz with right answers. Practice explaining why you choose each pattern, not just what it is. Every decision has context — show that you understand when and why to apply each approach.