Android · 14 min read

Android architecture interview questions: layers and modules

Ten Android architecture interview questions and how senior answers are scored: layers, the domain layer, UDF, Hilt, modules, navigation and testing.

Android has an official answer to “how should I architect my app”: Google’s architecture guide, with a UI layer, an optional domain layer and a data layer. That makes the Android architecture round harder, not easier. Everyone can recite the guide. The interviewer wants to hear where you followed it, where you did not, and why.

What the round scores

Signal Strong Weak
Decision Chooses for this app and this team Recites the official guide
Reasons Team size, testing, the app’s age and complexity “Google recommends it”
Cost Names what the choice makes harder “Clean architecture has no downsides”
Change Says when they would do it differently Defends every layer for every screen

A decision matrix

Situation Reasonable default What it costs
Small app, one or two engineers UI layer and data layer, no domain layer; manual DI or a small Hilt setup Logic may leak into ViewModels as it grows
Team of five to ten UI, data, and use cases only where logic is shared; Hilt Code generation time (Hilt supports KSP); Hilt knowledge required
Several teams Feature modules split into api and impl, core modules, convention plugins in Gradle Build configuration work; interface discipline
Large legacy app New features in the target architecture, old code migrated when touched Two styles side by side for a long time

The questions

1. “Walk me through your app’s architecture.”

Weak: the three layers from the documentation, with no mention of the actual app.

Senior: the layers as they exist in the app, then the decisions: “We have no domain layer except in checkout, where three ViewModels share the pricing rules. Repositories own Room as the source of truth and expose Flows. The ViewModel exposes one StateFlow of UI state per screen.” Then one thing they would change. The follow-up: “A response arrives after the user switched accounts.” Which writer is allowed to update current state? A layer diagram does not answer that; a rule at every writer does.

2. “Do you need a domain layer?”

What is scored: willingness to say no.

Senior: “Only where logic is shared between ViewModels or complex enough to deserve its own tests. A use case that only forwards a repository call adds a file and a layer of indirection for nothing. The official guide calls the domain layer optional for this reason.”

3. “What is unidirectional data flow, in your own code?”

Senior: state flows down from the ViewModel as a single immutable UI state; events flow up from the UI as function calls or an event type; the ViewModel is the only place state changes. Then the practical benefit: “A bug report with a screenshot maps to exactly one state value, which I can reproduce in a test.” The follow-up: “How do you handle one-off events like a snackbar?” The current guidance is to model them as state that the UI consumes and then reports as handled, rather than a Channel that can lose events during a configuration change. A senior candidate knows why the recommendation moved.

4. “Why Hilt? What does it cost?”

Senior: compile-time checked graph, scoping tied to Android components, standard in Jetpack integrations. Cost: code generation time in builds (Hilt supports KSP, which is faster than kapt), errors that are hard to read for newcomers, and ceremony in small apps. An alternative for small apps is manual injection through a container class. Naming the cost is the whole point of the question. A repository that holds account data also needs a reset on sign-out, whatever its scope. The follow-up: “Which scope do you give a repository?” Start from no scope: a new instance per injection is cheap, and @Singleton has to earn its place with state that really is app-wide or an object that is expensive to build. Then show you know the failure modes. Scope a shared cache to each ViewModel and two screens show different data. Make an object that holds one screen’s state a @Singleton and the next screen opens with stale state, and the singleton keeps it alive.

5. “How do you modularise for several teams?”

Senior: feature modules with an api module (interfaces, navigation routes, models the outside needs) and an impl module, so features depend only on each other’s api. Core modules for networking, database, design system. The app module wires implementations. Gradle convention plugins keep build files consistent. Measure build time before and after. The weak answer splits by layer: :ui, :domain, :data. It looks clean on a slide, but one feature change now edits three modules, every team merges into the same ones, and no module has an owner. With four or five engineers I would also say that heavy modularisation may not pay for itself yet. Some rules apply at any size: no feature depends on another feature’s impl, core does not depend on features, and there is no :core:common catch-all. Put those in a CI graph check so a violation fails the build instead of waiting for a reviewer.

Staff: ownership of core modules, a review rule for changes to shared api modules, and how you stop the “common” module from becoming a dumping ground.

6. “How does navigation work across feature modules?”

Senior: each feature exposes its destinations through its api module (type-safe routes with Navigation Compose, for example), and the app module assembles the graph. A feature never references another feature’s screen class directly. Deep links resolve to routes, not to activities. An id in a route is a lookup input, not permission: check the account and whether the resource still exists before showing it. If the user is signed out, keep the target, sign in, then evaluate it again with current facts instead of restoring a saved stack.

7. “Where do you put a network call that must survive the user leaving the screen?”

What is scored: matching the scope to the lifetime of the work.

Senior: not viewModelScope, which dies with the screen. For work that must finish, WorkManager. For work tied to the app process while it lives, an application-scoped CoroutineScope injected where needed. The answer depends on whether the work must survive process death. Leaving a screen and destroying its ViewModel are not always the same event, so name the scope you mean. If the work must survive process death, write the operation to disk first and schedule it second.

8. “How do you test this?”

Senior: ViewModel tests with fake repositories and a test dispatcher; repository tests against an in-memory Room database; a few Compose UI tests for critical flows; screenshot tests for the design system if the team maintains one. Fakes over mocks for repositories, because a fake keeps behaving like the real thing as tests grow. Say what you would not test. If you use a fake, run the same contract tests against the fake and the real implementation, so the fake cannot drift.

9. “A colleague wants to migrate everything to a new pattern. What do you do?”

Staff-level: ask what problem it solves and how you would measure the improvement; propose migrating incrementally, screen by screen when they are next changed; set a boundary between old and new. Say which side is authoritative at each step: two writers to the same data is its own bug, and a flag that hides the new screen can leave its worker running. A big-bang migration needs a business reason, not an architectural preference.

10. “What would you change in your current architecture?”

The honest answer wins: a decision that aged badly, how you noticed, and what changing it would cost now. “Nothing” loses.

The follow-up behind every question

“What does that cost you?” Prepare a cost for every choice. The iOS version of this round is iOS architecture interview questions, and the round these answers feed into is Android system design interview.

Sources

Free guide: the Phoenix Method