← Back to list

Cracking the Tesco Android Engineer (L2) Interview: System Design, Loyalty Apps, and Caching

If you’ve been following my recent deep-dives into senior Android interviews, you’ve seen a pattern: companies are rigorously testing how…

Jay Patel · 2026-05-23 10:55 · 6 claps · 4.8 min read
#android #android-app-development #tesco #job-interview #interview-questions
Open on Medium ↗

Cracking the Tesco Android Engineer (L2) Interview: System Design, Loyalty Apps, and Caching

Image generated by Google Gemini

Image generated by Google Gemini

If you’ve been following my recent deep-dives into senior Android interviews, you’ve seen a pattern: companies are rigorously testing how well you understand the modern Android ecosystem. But while some interviews focus on theoretical architecture, others want to see how you build a feature from the ground up.

Recently, I went through an Android Engineer L2 interview with Tesco. Given Tesco’s massive retail footprint, it’s no surprise that the technical round leaned heavily into Loyalty Systems, E-commerce System Design, and Offline-First Architectures. Instead of just firing off trivia questions, the interviewer presented a practical scenario: Design a brand-specific loyalty campaign feature. Here is a breakdown of how we architected the solution, handled edge cases, and tackled the live coding challenge.

1. The Scenario: Designing a Targeted Loyalty Campaign

Before writing a single line of code, we discussed my previous experience with US-based loyalty applications, multi-module project structures, and handling transaction-based reward points.

Then came the core system design task: The Prompt: Design a feature to support a brand-specific campaign that gives users bonus points based on a target purchase amount within a set campaign period. You have a single API to fetch this data. How do you architect the flow from the backend to the UI?

The Architecture Breakdown: For a feature like this, standard Clean Architecture layered with MVVM is the way to go.

  1. Data Layer: We define a CampaignRepository. It depends on a Retrofit API service to fetch the raw JSON.
  2. Domain Layer: This is crucial. The raw API response (which might have generic backend naming conventions) should not reach the UI. We create a Domain Model (e.g., CampaignProgress) and a Mapper to convert the raw DTO into usable business logic.
  3. UI Layer: The CampaignViewModel requests data from the repository (or Use Case) and exposes a sealed UiState (Loading, Success, Error) to the Jetpack Compose UI.

2. The Offline-First Challenge: Caching with DataStore

Once the basic architecture was laid out, the interviewer threw in a real-world constraint: The Constraint: Campaign data shouldn’t be fetched every time the user opens the app. Update it once per day, unless the user forces a refresh or clears the cache.

The Solution: While Room Database is great for complex relational data, a simple campaign state is a perfect candidate for Android DataStore (specifically Preferences DataStore).

Here is the caching strategy we discussed:

  • When the CampaignRepository is called, it first checks the DataStore for a last_fetched_timestamp.
  • If currentTime - last_fetched_timestamp < 24_HOURS, we read the cached campaign data directly from DataStore and emit it to the UI.
  • If it has been more than 24 hours (or the cache is empty), we make the network call, map the data, save the new payload and the new timestamp to the DataStore, and then emit the result.

3. Graceful Failures: Error State Management

A recurring theme in modern interviews is how you handle things when they break. The Question: How do you handle network failure or error states in your architecture to avoid crashes and provide correct UI feedback?

The golden rule is that Exceptions should not cross the boundary of the Data Layer. Inside the CampaignRepository, we wrap the network call in a runCatching block or a custom Result wrapper. If an IOException (no internet) or HttpException (server error) occurs, the repository suppresses the crash and returns a standardized Result.Error.

The ViewModel receives this, translates it into a UiState.Error("Please check your connection"), and Jetpack Compose reacts by rendering an error screen or a Snackbar, ensuring the app never actually crashes.

4. CI/CD, Compose, and Code Quality

We rounded out the architectural discussion by touching on day-to-day development workflows:

  • Compose Navigation: We discussed the transition from Fragments to Compose Navigation, and the trade-offs of using open-source wrapper libraries (like Compose Destinations or Voyager) for type-safe routing.
  • Testing Pipelines: Tesco places a strong emphasis on continuous integration. We discussed the importance of setting hard thresholds in the CI/CD pipeline, ensuring that pull requests are blocked unless unit test coverage hits that crucial 60–70% mark.

5. The Live Coding Challenge: Longest Common Prefix

The technical round concluded with a classic string manipulation problem (often found on LeetCode).

The Problem: Write a function to find the longest common prefix string amongst an array of strings. If there is no common prefix, return an empty string.

During the implementation, the interviewer provided great iterative guidance on string comparison logic. While you can solve this by sorting the array, the most efficient “horizontal scanning” approach in Kotlin looks like this:

fun longestCommonPrefix(strs: Array<String>): String {
    if (strs.isEmpty()) return ""

    // Start by assuming the first string is the common prefix
    var prefix = strs[0]

    for (i in 1 until strs.size) {
        // While the current string doesn't start with the prefix,
        // chop off the last character of the prefix and check again
        while (strs[i].indexOf(prefix) != 0) {
            prefix = prefix.substring(0, prefix.length - 1)

            // If we chop it down to nothing, there is no common prefix
            if (prefix.isEmpty()) return ""
        }
    }

    return prefix
}

// Or, the incredibly concise idiomatic Kotlin way using reduce:
// fun longestCommonPrefixKotlin(strs: Array<String>) = 
//     if (strs.isEmpty()) "" else strs.reduce { acc, s -> acc.commonPrefixWith(s) }

Final Thoughts

The Tesco L2 interview was a highly practical, scenario-driven assessment. It proves that companies aren’t just looking for developers who can write code; they are looking for engineers who can design robust features, respect network constraints, and build resilient, offline-capable applications.

What is your preferred caching strategy for simple API payloads? Let’s discuss in the comments below!

Appendix: The Complete Tesco L2 Question Bank

Want to run a mock interview with your peers? Here is the unedited list of concepts and questions covered during the session:

Experience & Domain Knowledge

  • Can you describe your recent contribution to the US-based loyalty application?
  • What kind of loyalty system did that app support? Was it general shopping points or associated with specific clients?

System Design & Architecture

  • Scenario: How would you design a feature to support a brand-specific campaign giving bonus points based on a target purchase amount within a set campaign period?
  • How will you architect the data flow from backend to UI? What class structure and naming conventions would you use?
  • Who is responsible for converting the API response to usable campaign progress data? (Clarification on Domain Models).
  • Scenario Modification: How would you modify your design to support offline data caching, updating campaign data once per day unless fetched on-demand (e.g., app reinstall or cache clear)?
  • How do you handle network failure or error states in your architecture to avoid crashes and provide correct UI feedback?

Modern Android & Tooling

  • Have you worked with Compose navigation, and what open-source libraries do you use?
  • What is your approach to test coverage and writing test cases for your implementations?
  • What is your experience with CI/CD pipelines, and what are your thoughts on setting test coverage thresholds (e.g., 60–70%) before raising pull requests?

Live Coding

  • Complete a coding task to find the longest common prefix among an array of strings.

You can find more of my code and projects on GitHub at Jaypatelbond or follow my ongoing technical ramblings right here at @jaypatelbond.

Happy Coding, and Happy Interviewing!


메타데이터
post_id
ea735d8c302c
slug
cracking-the-tesco-android-engineer-l2-interview-system-design-loyalty-apps-and-caching-ea735d8c302c
url
https://medium.com/@jaypatelbond/cracking-the-tesco-android-engineer-l2-interview-system-design-loyalty-apps-and-caching-ea735d8c302c
canonical_url
https://medium.com/@jaypatelbond/cracking-the-tesco-android-engineer-l2-interview-system-design-loyalty-apps-and-caching-ea735d8c302c
author_url
https://medium.com/@jaypatelbond
status
ok
fetched_at
2026-07-10 11:40:45