I submitted my CV and received a reply from a recruiter to schedule the first interview.
The first interview was a standard recruiter screen: an overview of the role, a discussion of my background, and general fit. It lasted around 20 minutes and was straightforward.
The second interview was a technical discussion covering Android and Kotlin fundamentals: Flow, coroutines, WorkManager, and related topics. This round lasted about an hour and was also fairly standard.
The third interview was a live-coding exercise. The task was to display JSON hotel data in a list, with a location header and the current date. This is where the process became much more problematic.
I completed the live-coding assignment using a modern Android structure: Jetpack Compose screens inside a NavHost, state observed from StateFlow exposed by the ViewModel, a repository abstraction, a fake data source, loading/error/data state, and basic navigation. I completed all stated requirements for the task.
The technical discussion afterward was far more intense than I would expect for a mid-level role. Most of the follow-up questions focused less on whether the implementation worked and more on very specific architectural preferences. I was questioned on why date formatting was done inside a composable, why I used a data class rather than sealed classes for UI state, why I injected a repository interface into the ViewModel, why I used Dispatchers.IO from the ViewModel, and what I meant by “domain” in the context of MVVM.
On the MVVM point specifically, I had meant that a domain layer can exist behind the ViewModel as part of the broader model/non-UI side of the architecture. The interviewer repeatedly pressed on the fact that “domain” is not part of the MVVM acronym, which is technically true, but the exchange became overly focused on terminology rather than the practical architecture being discussed.
Some of the questions were legitimate architecture probes, especially around dispatcher ownership and where responsibility should live. However, several points were presented in a very dogmatic way. For example, I was told that sealed classes are the Android-recommended way to model UI state. I had used a simple data-class state model:
```kotlin
data class HotelListState(
val hotels: List = emptyList(),
val error: String? = null
)
```
That is a common and valid approach, especially when state can be composed rather than strictly mutually exclusive. A sealed state model is also valid, but treating it as the single recommended or required approach felt overstated.
I was also told that try/catch is not the intended Kotlin approach for error handling and that runCatching should be used instead. Again, runCatching is a valid option, but presenting it as the required or “correct” Kotlin approach is too rigid. Try/catch is still standard Kotlin, and in coroutine-based code it can be preferable when cancellation or specific exception types need to be handled explicitly.
One example that stood out was the discussion around the date header. I had written:
```kotlin
Text(text = "Date: ${LocalDateTime.now()}")
```
The interviewer asked what was wrong with that line. I first pointed out that the raw string should be moved into string resources in production. He then pressed further and objected to LocalDateTime.now() being called inside the composable. While that is not how I would structure production code, it was a reasonable shortcut within the constraints of a live-coding exercise. The date requirement was trivial, the operation was effectively instantaneous, and the assignment was explicitly time-boxed.
This pattern repeated throughout the interview. Minor implementation details that would clearly be handled differently in production were treated as significant technical issues rather than reasonable shortcuts made during a live exercise. Instead of discussing tradeoffs, the interviewers often framed debatable choices as hard rules.
I also explained that in a production environment I would map the raw API response into a UI-friendly data model, expose only the fields the screen needs, and handle errors through a result type such as:
```kotlin
sealed class ApiResult {
data class Success(val data: T) : ApiResult()
data class Error(val exception: Exception) : ApiResult()
}
```
The ViewModel would then map success and error responses into UI state, while keeping composables focused on rendering state. Despite this, the debrief placed heavy emphasis on small architectural preferences and exact terminology.
Overall, this was probably the strictest Android interview I have had. The interviewers clearly cared about architecture and code structure, which is fair, but the tone of the technical debrief felt rigid and adversarial. Several debatable implementation choices were presented as objective standards rather than discussed as tradeoffs.
If you make it to the third interview round, I would recommend treating the live-coding exercise as if the code needs to be PR-ready, not merely functional and well-structured for a timed interview. Otherwise, expect heavy scrutiny over small implementation choices.
I would also suggest Priceline review how this live-coding round is conducted. A rigorous technical assessment is reasonable, but this process felt overly calibrated around the interviewers’ personal architectural preferences. It gave the impression that otherwise qualified mid-level candidates could be rejected for not matching a narrow internal style rather than for meaningful technical shortcomings.