Skip to content

Zepto Android App Development Interview Questions & Answers

Curated interview practice questions with answers. These are practice scenarios, not a verified list of past interview questions.

About Zepto hiring for Android App Development roles

Zepto is an Indian quick commerce company delivering groceries and daily essentials from dark stores within minutes. Its technology and analytics teams work on demand forecasting, inventory, dark store operations, delivery routing and app experience. It hires for software engineers, SDETs, business analysts, data analysts and data scientists.

For Android App Development roles, this Zepto practice set focuses most on Coroutines and Flow (7), Networking and APIs (7), App Architecture, MVVM and Hilt (4), Testing, Performance and Play Store (4). It has 2 easy, 15 medium, 13 hard questions, so you can start easy and work up to the harder ones.

Company type
E-commerce & consumer tech
Headquarters
Mumbai, India
Main India offices
Mumbai, Bengaluru

Zepto Android App Development interview process

  1. Online assessment or take-home assignment
  2. Technical or case round
  3. Hiring manager round
  4. Founder or leadership round

Typical pattern based on public candidate experiences. Rounds vary by role, team and year.

Solved for ZeptoStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

How should a Fragment or Activity collect a Flow safely?

Save to ▾
Show answer
Use lifecycleScope.launch with repeatOnLifecycle(Lifecycle.State.STARTED) so collection starts when the UI is visible and stops in the background, avoiding wasted work and crashes from updating views that are not ready. In a Fragment, use viewLifecycleOwner.lifecycleScope because the fragment view can be destroyed while the fragment lives. In Compose, use collectAsStateWithLifecycle(), which does the same lifecycle-aware collection.
02

Write a Room database class with a migration that adds a column.

Save to ▾
Show answer
@Database(entities = [NoteEntity::class], version = 2, exportSchema = true) abstract class AppDatabase : RoomDatabase() { abstract fun noteDao(): NoteDao } val MIGRATION_1_2 = object : Migration(1, 2) { override fun migrate(db: SupportSQLiteDatabase) { db.execSQL("ALTER TABLE notes ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0") } } val db = Room.databaseBuilder(context, AppDatabase::class.java, "app.db") .addMigrations(MIGRATION_1_2) .build()
03

Users on slow 2G and 3G networks in small towns report the app keeps failing. How do you improve it?

Save to ▾
Show answer
Set reasonable connect and read timeouts, retry idempotent requests with exponential backoff, and cache responses so screens show something offline. Reduce payload size with gzip, pagination and fields filtering, and load images in smaller sizes with placeholders. Show clear offline and retry states instead of generic errors. Test with the emulator network throttling and real devices on poor connections, and track failure rates by network type.
04

How do you share data between two screens in a checkout flow?

Save to ▾
Show answer
Options in order of preference: pass small IDs as navigation arguments and load the rest from a repository; store flow data in a repository scoped to the session; or use a ViewModel scoped to a nested navigation graph so all checkout screens share it and it is cleared when the flow ends. Avoid activity-scoped ViewModels holding everything, and avoid passing large objects through arguments.
05

How do you migrate from SharedPreferences to DataStore without losing user data?

Save to ▾
Show answer
Use SharedPreferencesMigration when creating the DataStore: preferencesDataStore(name = "settings", produceMigrations = { ctx -> listOf(SharedPreferencesMigration(ctx, "old_prefs")) }). On first access, DataStore copies the values and deletes the old file. Update all reads and writes to go through the new repository at once, because mixing both stores leads to inconsistent values. Test the migration with existing preference files.
06

A crash says IllegalStateException: Can not perform this action after onSaveInstanceState. What is happening?

Save to ▾
Show answer
A fragment transaction or dialog show was committed after the Activity saved its state, usually because a network callback finished while the app was in background. Committing then would lose state. Fix by triggering navigation only when the lifecycle is at least STARTED, collecting events with repeatOnLifecycle, or deferring the action. commitAllowingStateLoss hides the crash but can lose the transaction, so use it only when that is acceptable.
07

A ViewModel has grown to 1500 lines handling many responsibilities. How would you refactor it?

Save to ▾
Show answer
First add tests around current behaviour so refactoring is safe. Then extract business logic into use cases or domain classes, move data mapping into repositories or mappers, and split UI state into smaller, focused data classes. If the screen truly has separate areas, consider separate state holders or child ViewModels per section. Refactor in small pull requests so reviewers can follow and regressions are easy to locate.
08

Product wants a feature flag to turn a new checkout on or off without releasing an update. How do you design it?

Save to ▾
Show answer
Use a remote config service with defaults bundled in the app, wrapped in a FeatureFlags interface injected via Hilt. Fetch and cache values at startup with a sensible timeout, and read the flag at navigation decision points rather than deep inside UI code. Keep both checkout paths tested, log which variant users see for analytics, and plan to remove the old path and the flag after rollout.
09

QA says a bug happens only on one manufacturer's phones. How do you debug it?

Save to ▾
Show answer
Get exact device model, OS and skin versions, and steps. Use a remote device lab or Firebase Test Lab to reproduce if you lack the device. Collect logcat and bug reports, and compare behaviour around known differences like battery optimisation, autostart restrictions, custom permission dialogs or WebView versions. Add targeted logging or a non-fatal Crashlytics event, then apply a fix guarded by checks only if necessary.
10

Tell me about a time you worked closely with a backend team to solve a problem.

Save to ▾
Show answer
Situation: our feed took four seconds to load because the app made six API calls. Task: reduce load time. Action: I measured each call with network profiling, shared a timeline with the backend team, and we agreed on one aggregated endpoint with only the fields the screen used, plus pagination. I updated the client and added caching. Result: load time dropped to under one second and both teams reused the approach.
11

Users report battery drain after the latest update. How do you investigate?

Save to ▾
Show answer
Check Android vitals for excessive wake locks and wakeups, then use Battery Historian or the Energy Profiler to look at wake locks, alarms, location requests, jobs and network activity. Common causes are frequent location updates, a foreground service left running, polling instead of push, and WorkManager jobs scheduled too often. Fix by batching work, using lower location priority, stopping services promptly and adding proper constraints.
12

What do flatMapLatest, flatMapConcat and flatMapMerge do?

Save to ▾
Show answer
All map each value to a new flow and flatten the results. flatMapLatest cancels the previous inner flow when a new value arrives, perfect for search. flatMapConcat waits for each inner flow to finish before starting the next, preserving order. flatMapMerge runs inner flows concurrently up to a concurrency limit, useful for parallel downloads where order does not matter. Choosing wrongly can cause stale results or wasted network calls.
13

Your app shows duplicate orders when users tap Pay twice on a slow network. How do you fix it?

Save to ▾
Show answer
Disable the button and show progress once tapped, but do not rely only on the UI. Generate an idempotency key on the client for each checkout attempt and send it with the request so the server returns the same result for retries. Ensure the payment call is not retried automatically by OkHttp for non-idempotent POSTs, and let the user check order status if the response is lost.
14

What is the difference between remember and rememberSaveable?

Save to ▾
Show answer
remember keeps a value across recompositions but loses it when the composable leaves composition, on configuration change or process death. rememberSaveable stores the value in the saved instance state Bundle, so it survives rotation and process death; it works with types that can go in a Bundle or with a custom Saver. Use rememberSaveable for things like text input or selected tab, and a ViewModel for screen-level data.
15

What is the difference between Job and SupervisorJob?

Save to ▾
Show answer
With a regular Job, a failing child cancels its parent and all siblings. With a SupervisorJob, or inside supervisorScope, a child failure does not affect siblings or the parent; each child handles its own errors. viewModelScope uses a SupervisorJob, so one failed launch does not kill other work in the ViewModel. Use supervisor behaviour when tasks are independent, like loading separate widgets on a dashboard.
16

Write a Flow that combines user settings and product list into one UI state.

Save to ▾
Show answer
val uiState: StateFlow<HomeState> = combine( settingsRepo.settings, productRepo.products ) { settings, products -> HomeState( currency = settings.currency, items = products.sortedBy { it.price } ) }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), initialValue = HomeState() )
17

What is a repository and why is it the single source of truth?

Save to ▾
Show answer
A repository hides where data comes from and gives the rest of the app one API, such as getProducts(): Flow<List<Product>>. With an offline-first design, the repository reads from Room and refreshes from the network in the background, writing results to the database. The UI always observes the database, so it is the single source of truth. This avoids inconsistent copies of data across screens.
18

How does coroutine cancellation work, and why must code be cooperative?

Save to ▾
Show answer
Cancelling a Job sets it to cancelling state, and the next suspension point such as delay, withContext or a suspending library call throws CancellationException. A tight CPU loop with no suspension keeps running, so check isActive or call ensureActive() or yield() inside it. Do not swallow CancellationException in a generic catch block; rethrow it. Use finally or withContext(NonCancellable) for cleanup that must finish.
19

How do you write tests for a Room DAO?

Save to ▾
Show answer
Create an in-memory database with Room.inMemoryDatabaseBuilder in an instrumented test or with Robolectric, get the DAO, insert test data and assert query results. For Flow queries, collect the first value with first() or use Turbine. Close the database in @After. Test edge cases like conflict strategies and ordering. In-memory databases are fast and isolated, so each test starts clean.
20

What are launch modes and task affinity?

Save to ▾
Show answer
Launch modes control how an Activity instance joins a task. standard creates a new instance each time. singleTop reuses the instance at the top and calls onNewIntent. singleTask keeps one instance in its task and clears activities above it. singleInstance puts it alone in a task, and singleInstancePerTask exists since Android 12. Intent flags like FLAG_ACTIVITY_CLEAR_TOP give similar control. Most modern single-activity apps rarely need them.
21

The app's cold start takes 4 seconds. How do you improve it?

Save to ▾
Show answer
Measure first with Macrobenchmark StartupTimingMetric on a release build. Then reduce work in Application.onCreate by deferring SDK initialisation, using App Startup or lazy injection, and moving disk I/O off the main thread. Simplify the first screen, avoid blocking on network before showing UI, use the SplashScreen API properly, and add a Baseline Profile, which often improves startup by about 20 to 30 percent.
22

How do you handle a situation where you are blocked by another team's API not being ready?

Save to ▾
Show answer
I agree on the API contract early, with sample JSON, and build against a fake or mock server so my UI and logic are ready. I keep the repository interface stable, so switching to the real API is a small change. I communicate status in standups and raise risks to my lead early if the dependency threatens the deadline, suggesting options like releasing behind a feature flag.
23

How do you design an Android client to support API versioning and forced updates?

Save to ▾
Show answer
Send the app version in a header on every request, have the backend support older versions for a defined period, and use versioned endpoints for breaking changes. Add a remote config value for minimum supported version and use the Play In-App Updates API with immediate flow for critical updates and flexible flow otherwise. Show a clear message rather than failing silently when the app is too old.
24

What is a BroadcastReceiver and what limits apply since Android 8?

Save to ▾
Show answer
A BroadcastReceiver responds to broadcast intents. Since Android 8, most implicit broadcasts cannot be received by receivers declared in the manifest; you register them at runtime with registerReceiver while the app is running. A few exemptions exist, like BOOT_COMPLETED and locale change. Since Android 14, runtime receivers must specify RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED. onReceive runs on the main thread, so keep it short or use goAsync.
25

How do you build an adaptive layout for phones, foldables and tablets in Compose?

Save to ▾
Show answer
Use WindowSizeClass from the Material 3 adaptive library to choose layouts by width class, not device type. On compact width show a bottom navigation bar; on medium use a navigation rail; on expanded use a list-detail layout with ListDetailPaneScaffold or NavigationSuiteScaffold. Avoid locking orientation, since Android 16 ignores orientation restrictions on large screens. Test with resizable emulators and foldable postures.
26

Write a unit test for a ViewModel using a fake repository and runTest.

Save to ▾
Show answer
class ProductViewModelTest { @get:Rule val mainRule = MainDispatcherRule() @Test fun loadsProductsIntoState() = runTest { val fake = FakeProductRepository(listOf(Product("1", "Pen", 10.0))) val vm = ProductViewModel(fake) advanceUntilIdle() val state = vm.state.value assertFalse(state.loading) assertEquals(1, state.items.size) assertNull(state.error) } }
27

You need to sync 500 images to a server without overloading the network. How would you design it with coroutines?

Save to ▾
Show answer
Limit concurrency. Use a Semaphore with withPermit around each upload, or flatMapMerge(concurrency = 4) over a flow of files, or Dispatchers.IO.limitedParallelism(4). Use supervisorScope so one failed upload does not cancel the rest, collect failures for retry with backoff, and report progress through a StateFlow. If the sync must survive app exit, run it inside a CoroutineWorker with WorkManager constraints such as network connected.
28

Write a search flow that debounces user input and cancels stale requests.

Save to ▾
Show answer
private val query = MutableStateFlow("") val results: StateFlow<List<Item>> = query .debounce(300) .map { it.trim() } .filter { it.length >= 2 } .distinctUntilChanged() .flatMapLatest { q -> repo.search(q) } .catch { emit(emptyList()) } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList()) fun onQueryChange(text: String) { query.value = text }
29

How do you model API results and errors cleanly for the UI?

Save to ▾
Show answer
Map transport-level outcomes into a sealed result type in the data layer, for example Success(data), HttpError(code, message) and NetworkError. Catch IOException for connectivity, HttpException for non-2xx codes, and serialization exceptions for bad payloads, and never catch CancellationException. The ViewModel converts these into user-friendly UiState messages. Keep DTOs separate from domain models so API changes do not ripple through the UI.
30

How do you refresh an expired access token automatically?

Save to ▾
Show answer
Use an OkHttp Authenticator, which is called when the server returns 401. Inside it, synchronise so only one refresh runs at a time, call the refresh endpoint with the refresh token, save the new tokens and return the original request with the new header. If the request already carried the latest token and still failed, or refresh fails, return null and log the user out. Limit retries to avoid loops.

Tips to crack the Zepto Android App Development interview

  • Prepare cases on dark store operations, such as reducing stock-outs, improving picking time or choosing new store locations.
  • Practise SQL and Python for forecasting and inventory questions, including handling daily demand data.
  • Show comfort with a fast-paced startup environment and give examples of taking ownership in tight timelines.
  • Keep a one-minute story ready for every project on your CV: problem, your part, tools, result.
  • Practise the hard questions on this page aloud; Zepto interviewers usually ask follow-ups on your first answer.

Frequently asked questions

How many rounds are there in the Zepto Android App Development interview?

Candidates usually go through 4 stages: Online assessment or take-home assignment; Technical or case round; Hiring manager round; Founder or leadership round. The exact rounds change with the role, team, location and your experience, so confirm the process with the recruiter.

Which topics should I prepare for the Zepto Android App Development interview?

This practice set leans on Coroutines and Flow (7), Networking and APIs (7), App Architecture, MVVM and Hilt (4), Testing, Performance and Play Store (4). Revise those first, then practise explaining one or two of your own projects end to end, because most Zepto interviewers ask you to walk through real work.

Are these the exact questions asked at Zepto?

No. These are curated practice questions that match the skills and difficulty usually tested in e-commerce & consumer tech interviews like Zepto's. They are not a leaked or verified list of past questions.

Where does Zepto hire Android App Development professionals in India?

Zepto has large teams in Mumbai, Bengaluru. Openings and locations change often, so check the Zepto careers page and filter by city and experience.

Android App Development questions for similar companies