In simple Expo Go RN projects, is Debug APK testing required before Release APK testing?; Gemini chat

My Timesheet Tracker React Native and Expo app developed using packages that are compatible with Expo Go, gave me the easy and convenient option of using Expo Go - managed workflow, if I got the term correctly - for my main development and testing. To know more about the app, please visit my post: Using ChatGPT and Gemini to write React Native and Expo Timestamp app (mobile and web).

For the initial phase development, after the app got stable in Expo Go round of testing, I created a debug APK build and tested that. When I opened the debug APK installed app, it would open and then close immediately. At times, I was shown the error, "Timestamp Tracker keeps stopping". After fair amount of debugging with help from ChatGPT (CG), the issue got tracked down to (as per CG): "The crash is due to incompatible or mismatched Kotlin-based Expo module versions in your native Android build. Expo Go works fine because it uses its own bundled native code, but your bare/native build is broken."

I got the fix by running 'npx expo-doctor' which said that 1 check failed, details of which were:
The following packages should be updated for best compatibility with the installed expo version:
  expo@53.0.9 - expected version: 53.0.10
  react-native@0.79.2 - expected version: 0.79.3
Your project may not work correctly until you install the expected versions of the packages.
----

I followed expo-doctor's recommendation to run 'npx expo install --check' and said y to prompts/changes.

Next I recreated debug APK by running 'npx expo run:android'. That debug APK installed app did not have the earlier issue and worked as expected. I could see that the installed App icon was not what I wanted - this could be noticed only with debug APK (or release APK) as Expo Go does not install the app on the phone. I changed the icon to what I wanted, ran expo prebuild again and then recreated the debug APK. That debug APK install worked as expected including the App icon on phone/emulator being what I wanted.

Next I created release APK by doing required config setup and then running '.\gradlew.bat assembleRelease'. I installed the release APK on phone and emulator. It worked as expected.
In a review of this post's contents, Gem said: "You used .\gradlew.bat assembleRelease for the release APK build. While this works directly for Android, for a purely Expo-managed workflow that transitions to a bare workflow for builds, eas build --profile production (or npx expo build:android --type apk if using the classic build service) is the more "Expo-native" way to generate a release APK. However, running gradlew directly after expo prebuild is technically correct as expo prebuild creates the Android/iOS native directories. It just depends on which build system you primarily rely on after the bare workflow transition. Your use of it is fine."

So in this first phase of the app development, I had three top-level steps:
  1. Development and test using Expo Go (managed workflow)
  2. Test and bug fix cycle using Debug APK (bare workflow)
  3. Test (no bugs found) using Release APK (bare workflow)
In a second phase of app development, I made some UI and functionality changes in the app. For this development and test, I used Expo Go. After the app was working with my changes, I wondered whether I need to create a Debug APK and test or can go directly to release APK and test. I decided to skip the Debug APK creation and test.

After running expo prebuild, I ran npx expo-doctor. It gave one failed check:
The following packages should be updated for best compatibility with the installed expo version:
  expo@53.0.10 - expected version: 53.0.11
Your project may not work correctly until you install the expected versions of the packages.
----
I then ran 'npx expo install --check' and said y to prompts/changes. Ran 'npx expo-doctor' again. This time it did not report any issues.

I did required config setup, ran '.\gradlew.bat clean' and then ran '.\gradlew.bat assembleRelease'. It generated the release APK which I installed on phone (updated older app) and it worked as expected.

So for this second phase development work on the app, skiping the debug APK creation, test and bug fix cycle and going straight to release APK, seems to have worked out and saved me the time that debug APK creation and test would have taken.

I then had a chat with Gemini on this topic, which I have publicly shared: Skipping Debug APK step for Expo Go proj. The chat is longish. So I have given extracts from that chat below (some extracts are  slightly edited). Even these extracts are longish but I felt it appropriate to include it.

I asked: ... when Expo Go is the primary dev environment due to app using only packages compatible with Expo Go, I don't understand why additional step of debug APK is needed. Can you provide me examples of bugs which can be caught only in debug APK and not in Expo Go, given the app context that I have provided? [I had forgotten about the issue I myself faced in first phase of app which worked in Expo Go but did not work in Debug APK app and, quite surely, would not have worked in Release APK app.]

Gem: You bring up a very valid point, and it highlights a common misconception. When you're exclusively using packages compatible with Expo Go, it's easy to think that Expo Go is a perfect representation of your app. However, there's a fundamental difference between running your app inside the Expo Go client and running it as a standalone debug APK (or a local development build created with npx expo run:android/ios).

The Key Difference: Expo Go is a generic native wrapper; a debug APK is your specific native app.

Expo Go is a pre-compiled native application that includes a vast array of Expo modules. When you run your app in Expo Go, your JavaScript bundle is downloaded and executed within that pre-existing native environment. It abstracts away a lot of the native build process.

A debug APK, on the other hand, is your actual native application compiled from scratch, including only the native modules your project specifies and configured exactly as defined in your app.json/app.config.js and any other native build configuration files (even if generated by Expo's build process).

This distinction leads to scenarios where bugs can appear in a debug APK (or local dev build) but not in Expo Go, even when all your packages are "Expo Go compatible."

Here are some examples of bugs or issues that can be caught in a debug APK (or local dev build) but not necessarily in Expo Go:

  1. Deep Linking and Universal Links Configuration:

    • Expo Go: Deep links will typically use the Expo Go scheme (e.g., exp://your-username.expo.dev/your-app-slug?path=/some/route).
    • Debug APK: Your app's specific deep link schemes (e.g., myapp://some/route) and Universal Link configurations (associated domains on iOS, intent filters on Android) are only active and testable in a standalone build. A misconfigured app.json for deep links will only manifest as a bug when you try to open your debug APK via its custom scheme or universal link.
    • Example Bug: You set up scheme in app.json, but when you try to open myapp://settings on a device with your debug APK installed, it either opens the wrong app or doesn't open at all. This cannot be tested in Expo Go.
  2. Push Notification Setup (Initial Configuration):

    • Expo Go: Notifications sent via Expo's push notification service will work, as Expo Go handles the FCM/APNS credentials for itself.
    • Debug APK: Your app needs its own specific configuration (e.g., google-services.json on Android, entitlements and P8/P12 key on iOS) to receive push notifications directly. A bug in your app.json configuration related to expo.push.projectId or missing/incorrect google-services.json will prevent your debug APK from receiving notifications, even if the JS code is correct. This is crucial for verifying your entire push notification pipeline.
    • Example Bug: You send a push notification from your backend (using your project's FCM/APNS credentials), but your debug APK doesn't receive it, while Expo Go (running the same JS code) would have received Expo-sent notifications.
  3. App Icon, Splash Screen, and App Name Issues:

    • Expo Go: You'll always see the generic Expo Go icon and splash screen.
    • Debug APK: This is where your app.json icon, splash, and name configurations actually take effect. Bugs here aren't functional, but are critical visual issues.
    • Example Bug: Your custom font for the splash screen isn't loading correctly, or the app icon appears distorted on certain device densities. You can only see these in a standalone build.
  4. Native Permissions Prompt Wording and Behavior:

    • Expo Go: The permission prompts might originate from the Expo Go app itself, or the wording might be slightly different than what your standalone app would display based on your infoPlist or android.permissions configurations.
    • Debug APK: You can verify the exact wording and behavior of system permission prompts (e.g., location, camera, microphone access) as they will appear to your users.
    • Example Bug: Your location permission explanation string in app.json (ios.infoPlist.NSLocationWhenInUseUsageDescription) is missing or incorrect, leading to a generic or confusing system prompt.
  5. Font Loading and Asset Issues (Subtle Differences):

    • Expo Go: Assets and fonts are often resolved via Expo Go's internal mechanisms.
    • Debug APK: While rare with standard usage, sometimes custom fonts or specific asset loading patterns can behave differently when bundled directly into your app's native binary. This could be due to native linking, asset bundling differences, or subtle timing issues.
    • Example Bug: A custom font loads perfectly fine in Expo Go, but in the debug APK, it appears as the default system font on some Android devices, indicating a native asset linking issue.
  6. Background Task Registration and Execution (Subtleties):

    • Expo Go: Background tasks might behave slightly differently due to Expo Go's own lifecycle management.
    • Debug APK: For tasks like background location tracking or specific notification handling when the app is killed, the exact native registration and lifecycle management is crucial. Testing this in a standalone debug build ensures your native code behaves as expected when the app is truly independent.
    • Example Bug: A background location tracker works in Expo Go, but in the debug APK, it stops working after the app is swiped away from recent apps on Android, indicating an issue with persistent service registration in a standalone context.
  7. app.json or app.config.js Build-Time Configuration Errors:

    • Many critical configurations (e.g., android.package, ios.bundleIdentifier, plugins, hooks) only apply when you build a standalone app. Errors in these configurations will only manifest during the build process or when running the resulting APK.
    • Example Bug: You misspell a plugin name, or your android.package conflicts with another app on the device. This won't affect Expo Go, but will prevent your debug APK from installing or running.

Why not just "debug with Expo Go if release APK has bugs"?

You're right that for JavaScript-level bugs, you can often go back to Expo Go. However, the examples above demonstrate issues that are:

  • Native Configuration Dependent: They rely on how your app is compiled natively, not just the JavaScript.
  • Environment-Specific: They arise from the app being a standalone entity rather than running inside a generic wrapper.
  • Difficult to Replicate in Expo Go: You simply cannot test these specific native behaviors or configurations within the Expo Go client.

In summary:

Testing a debug APK (or local dev build) serves as a crucial intermediate step. It allows you to:

  • Verify your native build configurations before investing in a release build.
  • Catch environment-specific native bugs that Expo Go's generic wrapper might mask.
  • Test the full end-to-end user experience of your app as a standalone entity, including things like deep linking, push notifications, and app identity.

By catching these types of issues early in a debug APK, you save significant time and frustration compared to finding them only after creating and distributing a release APK [Ravi: release will be distributed only after release APK test does not find bugs. So this point will not apply fully. end-Ravi] It ensures that your JavaScript code works correctly within the context of your specific native app build.

Me: Thanks for the specific examples. Clearly there are scenarios where the release APK behaviour will be matched by the debug APK behaviour but not Expo Go behaviour. But I would argue that if stringent testing of release shows bugs, those bugs can be attempted to be replicated in Expo Go and then fixed. Only if Expo Go fails to replicate those bugs, should the Debug APK step be used.

U see, time is the big factor here. Testing on Expo Go, Debug APK and Release APK will cost too much time. To reduce development & test cost, either one should go with Expo Go and Release APK with Debug APK used only if Expo Go cannot replicate bug seen in Release APK testing, OR one should drop Expo Go completely even if all the packages of the project are compatible with Expo go, and use only Debug APK and Release APK. Thus we will not have three full rounds of testing which may be too expensive in time for a project.

Gem: You've hit on a critical point in software development: balancing thoroughness with efficiency and time-to-market. Your concern about "three full rounds of testing" is absolutely valid, as repeated, identical testing can be costly.

Let's break down your proposed workflows and refine the approach to optimize for both speed and stability.

Analyzing Your Proposed Workflows

Option 1: Expo Go -> Release APK (use Debug APK only if Expo Go fails to replicate bug)

  • Pros:
    • Maximizes the initial rapid development speed of Expo Go.
    • Potentially faster for very simple apps with minimal native interaction, or for minor JS-only hotfixes.
  • Cons (and why it's generally risky):
    • Difficult Debugging: Debugging a minified, optimized Release APK is exceptionally difficult. You'll likely spend much more time trying to pinpoint the issue without proper tools.
    • [Snipped some points]

Option 2: Drop Expo Go entirely, use only Debug APK -> Release APK

  • Pros:
    • This is the traditional, robust mobile development workflow.
    • You catch native/configuration issues early (in the Debug APK phase) before the release build.
    • The Debug APK is fully debuggable with proper tools.
    • Ensures that your app behaves as a standalone entity from the start.
  • Cons:
    • Sacrifices the incredible speed of Expo Go for JavaScript-only iteration. Building a local development build (Debug APK) takes more time than scanning a QR code with Expo Go.
    • Can feel slower for everyday development iterations, especially for UI tweaks or simple logic changes.

A More Balanced and Efficient Workflow

The goal shouldn't be "three full rounds of identical testing," but rather targeted testing at each stage that leverages the strengths of that stage while addressing its limitations.

Here's a refined workflow that balances speed, stability, and efficiency:

  1. Stage 1: Expo Go for Rapid JS Development & Iteration (Primary Development)

    • Purpose: Ideal for building out core features, UI/UX, business logic, and general JavaScript functionality. This is where you spend 80-90% of your development time.
    • Testing Scope:
      • Unit testing of individual components/functions.
      • Functional testing of features that are purely JavaScript or rely on Expo Go's stable native modules.
      • Visual verification of UI.
    • Benefit: Incredibly fast feedback loop.
    • When to transition: When a significant new feature set is complete, or you're nearing a release candidate.
  2. Stage 2: Local Development Build (Debug APK) for Native Integration & App-Specific Configuration Testing

    • Purpose: To verify that your app behaves correctly as a standalone native application, addressing the "native configuration" and "app lifecycle" issues we discussed.
    • Testing Scope (Targeted, NOT a full re-test of everything):
      • Verify Native Configurations: Deep linking, push notifications (full end-to-end flow), app icon, splash screen, app name.
      • Test System Permissions: Ensure prompts are correct and permissions are handled properly.
      • Background Tasks/Services: Verify behavior when the app is in the background or killed.
      • Native Module Edge Cases: If using community modules, verify their behavior in a compiled environment.
      • Platform-Specific Quirks: Test specific Android/iOS behaviors that might differ from Expo Go.
      • Performance (Preliminary): Get a sense of how the unoptimized native app performs.
    • Benefit: Catches critical standalone app issues before a release build, making them easier to debug. This is a proactive step.
    • Frequency: Not for every small change, but certainly for major feature complete milestones, before embarking on a release build, or if you've touched app.json/native configurations.
  3. Stage 3: Release APK for Final QA & Performance Testing

    • Purpose: The final quality gate to ensure your app is optimized, stable, and ready for public distribution.
    • Testing Scope (Comprehensive, but building on previous stages):
      • End-to-End User Flows: Test all critical paths as a real user would.
      • Performance Benchmarking: Check app responsiveness, load times, memory usage (since it's optimized).
      • Bug Regression: Ensure no previous bugs have reappeared due to minification or optimization.
      • Network Behavior: Verify caching, error handling, and security (e.g., SSL pinning if applicable).
      • Analytics & Crash Reporting: Confirm these are correctly integrated and firing.
      • Monetization/In-app Purchases: Crucial to test in the release build.
    • Benefit: Ensures a high-quality product for your users, minimizing post-release issues.
    • Frequency: Every time you plan to publish a new version to app stores.

Conclusion: It's About Complementary Testing

The key is that these three stages are complementary, not redundant.

  • Expo Go gives you speed for your JavaScript logic.
  • Debug APK (local build) gives you confidence in your native integration and app configuration.
  • Release APK gives you assurance of production quality and performance.

Trying to skip the Debug APK step entirely and rely on the Release APK for the first "real app" test is a gamble. While it might save a few hours upfront, it significantly increases the risk of encountering hard-to-debug issues in production, which can cost days or weeks in fixes ....

For most projects, incorporating a targeted Debug APK testing phase before the final Release APK build is a wise investment that ultimately saves time and headache.

Me: Great analysis!

The Debug APK testing Scope being Targeted and NOT a full re-test of everything is a crucial point. I have a better understanding about your view now.

Gem: [On risks of Debug APK step being skipped]

1. Increased Cost and Difficulty of Debugging at the Last Stage: * Finding a bug in a Release APK means debugging a minified, optimized build where tools are severely limited. This process is inherently more difficult and time-consuming than debugging the same issue in a Debug APK, where you have full access to debugging tools, un-minified code, and verbose logs.

2. Higher Stakes and Pressure: * When a critical bug is found during Release APK testing, it puts immense pressure on the team. You're at the very last gate before shipping, and any delay or re-spin impacts your release schedule. Finding the same bug earlier in a Debug APK allows for a more relaxed and efficient debugging process.

3. Reduced Confidence in the Final Build: * If significant "native/configuration-related" bugs are consistently surfacing only in the Release APK testing phase, it indicates a gap in your earlier testing stages. While they might be caught, it suggests a less robust overall process and can lead to less confidence in the final product.

So, while stringent Release APK testing is indeed your ultimate safeguard against releasing a faulty product, the Debug APK testing phase acts as a proactive filter. It's designed to catch those specific types of native/configuration bugs earlier in the development cycle, when they are significantly cheaper, easier, and less stressful to identify and fix. This prevents them from ever reaching the high-stakes Release APK testing phase, making that final stage smoother and more focused on performance and general regression.

Me: I think it all depends on the project environment. if one is doing a simple app and the app userbase is more tolerant of some bugs in the app but wants the app quickly and at low cost, then skipping debug APK testing unless Expo Go cannot replicate it, should be OK. You wrote, "Finding a bug in a Release APK means debugging a minified, optimized build where tools are severely limited. " - Well, in the approach I have used for a simple app, I do not even attempt to debug the release APK. I simply try to reproduce the bug in Expo Go environment and then debug it and fix it. ... In my Timestamp Tracker app, as it is so simple, I think not using Debug APK testing for an enhancements phase that I worked on, has delivered results in time saving . Further, even if some bugs do not get caught in release for this simple app, it is not that big an issue. I mean, I am offering it free and rather than go for absolutely minimal bugs in production (as no bugs is virtually impossible), I am OK with few bugs slipping into production and then getting fixed later in a patch release.

Gem: You've articulated a perfectly valid and often necessary approach, especially for certain types of projects. You are absolutely correct: the "best" approach is highly dependent on the specific project environment, its goals, the nature of the app, and the expectations of its user base.

Your points are well-taken and highlight a conscious, strategic decision-making process:

  1. Project Context (Simple App, Tolerant User Base, Free App): For a simple, free app where users are more tolerant of minor imperfections and the priority is quick delivery at a low cost, the traditional, highly rigorous multi-stage testing process might indeed be overkill. The return on investment for eliminating every single potential bug might not justify the additional time and resources.

  2. Debugging Strategy (Reproduce in Expo Go, then Fix): Your strategy of not attempting to debug the minified release APK directly, but instead trying to reproduce the bug in the more developer-friendly Expo Go environment, is a smart and practical workflow for simple apps. If the bug can be reliably reproduced in Expo Go, you get all the debugging benefits. If it can't, then you'd look into a Debug APK or more advanced native debugging, but you're only doing so when absolutely necessary. This minimizes the "cost" of the Debug APK step.

  3. Acceptance of Minor Production Bugs and Patch Releases: This is a key differentiator. For apps with a high tolerance for minor bugs and a willingness to quickly release patches, the emphasis shifts from "preventing all bugs upfront" to "fast iteration and effective post-release support." This is a common and acceptable model, especially in early-stage products or projects where agility is paramount.

You've successfully adopted a lean development and testing strategy that aligns with your project's specific constraints and objectives. Your approach for the Timestamp Tracker app demonstrates that:

  • You've identified the critical factors (simplicity, user tolerance, cost, speed).
  • You've chosen a workflow that optimizes for those factors.
  • You have a clear plan for handling bugs that do surface (reproduce in Expo Go, fix, patch release).

In your specific scenario, where the app is simple, free, has a tolerant user base, and you prioritize speed and cost, skipping routine Debug APK testing in favor of Expo Go and then Release APK testing (with Debug APK as a fallback for non-reproducible bugs) is indeed a perfectly acceptable and pragmatic approach. It's about making intelligent trade-offs, and you've clearly made them for your project.

Me: I think your above analysis is very correct. Your earlier responses had very good points which are learnings for me, especially about categories of bugs that may not happen in Expo Go but will show up in Debug APK (and release APK). It was very helpful for me to run my thoughts through you. ...  Thank you so much.


Comments