State of the Costco Android App
An amber health status overall. Architecture, build modernity, and DI are strong. Stability, accessibility, and modernization carry material risk that needs two focused quarters of investment to bring to industry parity.
Headline indicators
At a glance
Severity distribution
Health by area · sorted
Industry-parity gap
Status by area
Twelve health areas grouped by domain. Each card describes what the area means, where the codebase stands today, why it matters to the business, and the recommended next step. Color stripe at the top of each card encodes status — green / amber / red.
How likely the app is to crash or freeze on real devices in production.
39 critical-severity defects across the codebase — Kotlin !! force-unwraps, unsafe as casts, unguarded getActivity() calls, list-index access without bounds checks. 96 memory-leak risk patterns additionally — Handler.postDelayed without cleanup, WebViews not destroyed, untracked coroutine Jobs. Concentration in MainWebViewFragment, FindAStoreFragment, MainActivity, WarehouseOffersFragment.
Each unhandled crash is a transaction lost during peak shopping hours. Sustained Crash-Free-Users decline cuts Play Store ranking, drives 1-star reviews, and erodes member trust. Memory leaks compound during long sessions and during the holiday shopping push.
Stability strike-team — 2 senior engineers × 4 weeks fixing the top-20 by realized Crashlytics impact. Target: +2 percentage points crash-free-users within 1–2 release cycles.
Resistance to data theft, fraud, abuse, and tampering.
Strong baseline: HTTPS-only enforced, AES-256-GCM AndroidKeystore for tokens, biometric auth, R8 obfuscation, backup disabled, NowSecure scans in CI. Gaps: WebView lockdown incomplete (setAllowFileAccess(false) not set; addJavascriptInterface + dynamic loadUrl("javascript:…") in offers screen), no Play Integrity API, no FLAG_SECURE on payment / DMC screens, no data extraction rules file, certificate pinning import found but use needs verification.
WebView and dynamic JS-injection patterns are the typical entry points pen-testers find first. A successful exploit could exfiltrate session data or hijack payment flows. Lack of Play Integrity removes a key signal against fraud rings.
Two-week WebView lockdown sweep + Play Integrity adoption + FLAG_SECURE on payment screens. Verify cert-pin activation. Address ahead of the next NowSecure scan.
Adherence to privacy law (GDPR / CCPA / PIPEDA) and Play Store policies.
~38 third-party SDKs shipping data off-device — Adobe Marketing Cloud (11 modules), Firebase, NokNok FIDO2, ThreatMetrix fingerprinting, Mastercard ClickToPay, Lucidworks, Contentstack. OneTrust integrated for consent. Email persisted unencrypted in DataStore; lat/lng cached on device. ACCESS_BACKGROUND_LOCATION used for warehouse geofencing — Play Store-sensitive permission. No SDK manifest or data-flow document.
Background-location reviews block releases when policy refreshes. CCPA / PIPEDA exposure if consent flow does not gate analytics. Data Subject Access Requests (DSARs) become engineering emergencies without a documented PII map.
Verify Firebase Consent Mode v2 and Adobe consent flags gate analytics correctly. Encrypt cached email via the existing KeyStoreManager. Publish an SDK manifest + privacy disclosure update.
How fast the app feels — startup, scrolling, memory.
20+ dependencies eagerly initialized in CostcoApplication. Dual image libraries (Coil + Glide) and dual networking stacks (Volley + Retrofit/OkHttp) ship together. No baseline profile, no Macrobenchmark in CI. 8 runBlocking calls in production code (NewOnboardingViewModel alone has 8). 96 memory leak risk patterns trend memory upward in long sessions.
Slow cold start translates directly to lower engagement and retention. Play Vitals "slow start" + "slow rendering" flags reduce store visibility. OOM crashes on memory-constrained Android devices (still ~30% of the market) are unaddressed.
Defer non-critical Application init paths via androidx.startup.Initializer. Generate a baseline profile for Home → PDP → Cart and ship it. Remove duplicate stacks (Glide and Volley).
Ability for users with disabilities — vision, motor, cognitive — to use the app.
Near-zero contentDescription / Compose semantics adoption. WCAG 2.2 conformance: 3 FAILs (contrast 1.4.3, text resize 1.4.4, target size 2.5.5), 4 PARTIALs. No values-night across most modules. No font-scale 200% snapshot tests. No 48dp minimum target enforcement. RTL-locale resources absent.
AODA (Canada) / ADA (US) / Section 508 lawsuit exposure. Members with vision, motor, or cognitive impairments cannot reliably complete shopping flows. Brand reputation hit if reported by accessibility advocacy organizations. Failed audits block partnership with public-sector buyers.
6-week accessibility sprint: 1 design-system + 2 feature engineers. Codemod for contentDescription across all Image/Icon; contrast validation in design-token build; Modifier.minimumInteractiveComponentSize() applied via a shared rule.
How well the app serves users in different languages and regions.
Currently English + French (incl. Canadian variants en-rCA, fr-rCA). 693 locale resource directories total, 439 strings in main module. No RTL-locale resources. No LocaleManager.setApplicationLocales (Android 13 per-app language). No pseudo-locales in debug. Currency / date / plural formatters not consistently used (NumberFormat, DateTimeFormatter, getQuantityString).
Future Costco market expansion (Mexico, UK, Australia, Iceland, Sweden, Spain, Korea, Japan, Taiwan) means locale-specific bugs surface in production at scale. RTL-blind layouts will fail Hebrew / Arabic markets if those become targets.
Add pseudo-locales (en_XA, ar_XB) in debug builds. Audit string concatenation of currency / dates / counts. Plan a one-week RTL audit pass when expansion targets firm up.
Quality of the codebase's structure, modularity, and decoupling.
56 modules (1 app + 22 features + 30 shared + 3 core/lib). Hilt DI with 60+ modules, repository + UseCase + ViewModel layers consistent in feature modules, version catalog. Concerns: shared/sdui exposes 209 public declarations with <1% internal; three util modules (util, blankutil, common) with overlapping responsibilities; MainActivity.java is a 5,411-line god-object.
Strong foundation lets the team ship in parallel. The few hotspots (MainActivity, sdui leaky surface) concentrate change cost and review friction, but the structure overall is healthy.
Convention plugins in build-logic/ for android-feature, android-library, compose-feature. Sweep internal modifier into shared modules. Begin a multi-quarter MainActivity refactor.
How much of the code is verified by automated tests.
~170 unit tests + ~80 instrumentation tests; modern stack (MockK 1.14.9, Turbine 1.2.1, kotlinx-coroutines-test 1.10.2, Compose UI Test, Karumi Shot screenshots, Hilt-testing). JaCoCo threshold 40.81% (low for a retail-scale app). Hilt-testing pinned at 2.28-alpha while Hilt 2.56 in main — version drift. MainCoroutineRule duplicated in 7 modules. JUnit 4 + 5 both present. No Macrobenchmark, no Firebase Test Lab in CI.
40% coverage means ~60% of changes don't run through any test gate. Test/runtime version drift can produce subtle DI bugs that pass tests but break in production.
Stair-step JaCoCo threshold to 60–70% over two quarters; consolidate MainCoroutineRule into a shared/testfixtures module; bump Hilt-testing to 2.56; pilot Macrobenchmark.
How current the toolchain is — Gradle, AGP, Kotlin, dependencies.
AGP 8.13.2, Kotlin 2.2.0, Gradle 8.14.4, version catalog with 126 entries, R8 enabled in release, Compose BOM 2026.02.01, Hilt 2.56. compileSdk 36, minSdk 31, targetSdk 36. App Bundle structure works. Some bleeding-edge versions (Compose BOM Feb 2026) — verify Compose Compiler matches K2.
Modern toolchain unlocks new Android features quickly, applies security patches automatically, and helps recruiting. Real strength to protect.
Add build-logic conventions to centralize plugin + Kotlin compile options. Pin known-good combinations. Enable build cache + configuration cache.
Accumulated work owed to the codebase that slows future work.
9,039 Java files vs 4,347 Kotlin (68% Java by file count). 197 TODO/FIXME/HACK markers across 88 files. Vendored DSLV + Raizlabs UniversalAdapter (third-party libraries inline). MainActivity.java at 5,411 lines. Dual stacks (Coil+Glide, Volley+Retrofit, Room+DBFlow). 30 shared modules with overlap. Stale README references SourceTree workflow, no ADRs, no per-module READMEs.
Each new feature ships a little slower; refactors are riskier; engineer onboarding and hiring become harder. The drag is invisible quarter-to-quarter but compounds.
Migrate MainActivity.java + CostcoApplication.java to Kotlin first (highest leverage). Retire Volley + DBFlow + Glide. Consolidate util modules. Set quarterly Java-LoC reduction targets.
How fast the team can ship features safely.
56 modules buildable in parallel (good for incremental builds). No build-logic / convention plugins — each module's gradle file is independent and prone to drift. Azure Pipelines CI with NowSecure scans. No Detekt / Ktlint baseline at the repo root. No CODEOWNERS — reviews are unrouted. JaCoCo 40% threshold doesn't actively gate. Hardcoded Maps API keys in committed gradle.properties create onboarding friction.
Reviews take longer than necessary; regressions slip through; new engineers spend their first week chasing setup issues instead of shipping.
Add convention plugins; CODEOWNERS at the repo root mapping modules to teams; Detekt + Ktlint with baselines; Codecov / SonarCloud per-PR coverage diffs.
Visibility into production behavior — logs, metrics, alerts, dashboards.
Firebase Crashlytics + Firebase Performance + Firebase Analytics integrated. Adobe Analytics + Adobe Target + Adobe Campaign (full marketing cloud). Timber + custom FirebaseRemoteLogger + ConsoleLoggerImpl. No clear PII scrubbing in release Timber tree. 8+ runBlocking sites are potential ANR sources without instrumentation. No defined error budget or rollback triggers.
Without consent-aware analytics, GDPR / CCPA risk. Without PII scrubbing, leaked logs become compliance issues. Without an error budget, the team can't make data-driven release decisions.
Add ReleaseLoggerTree with PII scrubbing patterns; verify Firebase Consent Mode v2; instrument repository latency to feed Performance dashboards; define a quarterly error budget.
Top risks (business framing)
Each risk is framed by likely business impact, suggested owner, and rough effort. Severity reflects probability × impact.
Crash exposure on critical shopping flows
Accessibility non-compliance
Tech debt drag on velocity
WebView attack surface
loadUrl("javascript:…") and bridge into native via addJavascriptInterface. If any HTML source is compromised or proxied, attackers could exfiltrate session data, call into the JS bridge, or redirect users. Pen-test priority.Heavy app startup
Background-location permission scrutiny
Stale documentation & onboarding
Recommended investment
Stability strike-team (now)
2 senior engineers x 4 weeks. Crash-free-users target +2.0 percentage points. ROI: directly visible in Play Store metrics within 1–2 release cycles.
Accessibility sprint (next)
1 design-system + 2 feature engineers x 6 weeks. Contains compliance risk; unlocks Costco-brand consistency. Concretely measurable via TalkBack acceptance tests.
DX investment (later)
1 platform engineer continuously. Convention plugins, build-logic, lint baselines. Pays back in faster CI builds + fewer flakes within 1 quarter.
Hold the line on basics
Maintain current strengths: Hilt DI, modularity, version catalog, build modernity, Coroutine discipline. These are real assets — protect them with conventions and PR gates.
Suggested roadmap
Now (next 30 days)
- Fix top-20 crash sites by Crashlytics impact (force-unwraps, getActivity guards).
- Lock down WebView surface:
addJavascriptInterfaceaudit + content allowlist. - Enable Detekt with twitter/compose-rules + Ktlint baseline gate in CI.
- Inventory background-location justifications & consent flow.
Next (30–90 days)
- Accessibility codemod:
contentDescription+ semantics across all Composables. - Defer 20+ Application init paths; ship a baseline profile for Home → PDP → Cart.
- Migrate
MainActivity+CostcoApplicationJava → Kotlin. - Standardize sealed
NetworkResult<T>across repositories.
Later (3–6 months)
- Convention plugins (
build-logic/) for android-feature / android-library / compose-feature. - Retire Volley; consolidate on Retrofit + Coil.
- ADRs for BFF, Compose adoption, Hilt scoping. Refreshed README.
- Per-feature Java→Kotlin burndown with quarterly LoC target.
Beyond (6+ months)
- Dark mode + dynamic color (Material 3 You).
- RTL-locale readiness; pseudo-locales in debug builds.
- Macrobenchmark + Firebase Performance dashboards in product reviews.
- Snapshot testing (Paparazzi) for design system regressions.
Where we stand vs. industry expectations
Solid bar shows current state; white tick shows industry expectation for a top-tier retail Android app. Gap analysis informs the roadmap above.
Quick reference
If you have 2 minutes
Look at the headline indicators above. The two reds (39 critical defects, 35/100 accessibility) are where leadership attention is most useful.
If you have 10 minutes
Skim the Top Risks. Each maps to a recommended owner and effort estimate. Use this to set quarterly priorities.
If you want to dig in
The technical Overview dashboard has 6 charts and Excel export of every finding. Class-by-class gives engineers their working list.
For the board / partners
This page is print-friendly — use your browser's Cmd/Ctrl-P to produce a clean PDF. Sidebar and toolbars are hidden in print mode.