Skip to content
ExponaBLOG
MODERN ANDROID & KOTLIN · DAY 069 / 100

Publishing to Google Play: practical tips and trade-offs

From build to store listing.

Expona Admin6 min read

Publishing to Google Play: practical tips and trade-offs

Getting your app to production is only half the battle—shipping it safely and professionally to millions of users requires understanding Google Play's signing, packaging, and review processes. We've learned from shipping dozens of apps across e-commerce, SaaS, and travel that rushing these steps leads to delayed releases, security vulnerabilities, or worse, being pulled from the store. Publishing to Google Play: practical tips and trade-offs is what separates a rough launch from a smooth one.

App Signing — Use Play App Signing and an upload key

Google Play offers two signing approaches: letting Google manage your app signing key (recommended), or managing it yourself. We strongly recommend delegating key management to Google through Play App Signing. Here's why: if you lose a private key you manage yourself, you can never update that app again on Google Play. Starting fresh means abandoning your install base, reviews, and rating history.

With Play App Signing, you generate an upload key that you control and use only to sign the AAB (Android App Bundle) you upload. Google extracts the upload key, stores your app signing key securely in their infrastructure, and re-signs the app before distributing it to devices. This separation means you can rotate your upload key if compromised, while Google maintains the permanent signing identity.

To set this up in your build configuration:

kotlin
android {
  signingConfigs {
    release {
      storeFile file("upload-keystore.jks")
      storePassword System.getenv("UPLOAD_KEYSTORE_PASSWORD")
      keyAlias System.getenv("UPLOAD_KEY_ALIAS")
      keyPassword System.getenv("UPLOAD_KEY_PASSWORD")
    }
  }

  buildTypes {
    release {
      signingConfig signingConfigs.release
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
  }
}

Store credentials in environment variables or a secure secrets manager, never in version control. When you first enroll in Play App Signing (which happens automatically for new apps), Google generates an app signing key and shows you its SHA-1 fingerprint. Keep that fingerprint documented—you'll need it if you ever integrate OAuth, Firebase, or other services that require key verification.

App Bundle — Upload .aab, not .apk

Android App Bundles (.aab files) are Google Play's modern packaging format. Unlike a monolithic APK, a bundle contains your app's code, resources, and assets organized by feature and density. Google's servers generate optimized APKs for each device type—a Pixel 6 gets a smaller download than a tablet, and users in different regions get localized resources only for their language.

The bundle format cuts install sizes by 15–35% on average. For e-commerce or travel apps where every byte matters on slow networks, this is tangible. Users notice faster downloads. Your store listing benefits from appearing more lean. And you reduce churn from users abandoning large installs.

Generating a bundle is straightforward:

kotlin
./gradlew bundleRelease

This produces app-release.aab in app/build/outputs/bundle/. Test it locally using bundletool before uploading:

kotlin
bundletool build-apks \
  --bundle=app-release.aab \
  --output=app.apks \
  --mode=universal

bundletool install-apks --apks=app.apks

The universal APK includes all languages, densities, and ABIs—useful for internal testing. In CI/CD pipelines, generate both universal APKs for testing and the bundle for upload. One gotcha: dynamic feature modules in your bundle won't install locally via bundletool in universal mode. You'll need Play Console's internal testing track to verify dynamic features work end-to-end.

Store Listing — Screenshots, description and privacy policy

Your store listing is users' first impression. A mediocre app with stellar marketing often outperforms a great app with no marketing. Screenshots, description, and privacy policy all influence conversion and, critically, whether Google approves your app for the store.

Screenshots: Provide at least 2, ideally 4–5 per locale. Show real user flows, not fake mockups. For a SaaS onboarding app, capture the exact screens users see: authentication, dashboard, first action. Annotate with text overlays ("Sync in seconds" or "Zero config required") to guide attention. Use tools like Screener or Appium with screenshot diff to generate consistent shots across multiple device sizes.

Description: Keep the first 80 characters punchy—that's what appears before "Read more." Lead with the job your app does: "Track expenses faster than spreadsheets" beats "A comprehensive expense management platform." Mention privacy features if relevant (e.g., "End-to-end encrypted" for messaging apps). Link to your privacy policy and terms of service; Google's policy reviewers check these.

Privacy policy: This is non-negotiable. Even if your app doesn't request permissions, having a clear, honest privacy policy is required. If you collect analytics, ads, or crash reports, disclose it. Google's machine learning now flags inconsistencies between what your app requests and what your policy claims. A common trap: saying "We don't collect location data" while your manifest includes ACCESS_FINE_LOCATION. Either remove the permission or update your policy. Use a privacy policy generator (Termly, iubenda) for a starting template, but customize it to your app's actual behavior.

Testing Tracks — Internal, closed and open testing before production

Google Play Console offers tiered testing environments. Shipping straight to production is reckless; we treat testing tracks as mandatory quality gates.

Internal testing: Your fastest feedback loop. Invite up to 100 testers (usually your team). Upload a new build and it's available to them within minutes. No review process. Use this for smoke tests after each release build. If a build crashes on launch, you catch it here, not in the app store.

Closed testing: Invite specific users or groups—early adopters, power users, or paying customers. Builds still don't go through review. This stage is ideal for beta features, UI polish, or validating a major refactor. Keep users here for 1–2 weeks minimum to gather real usage patterns. Collect feedback via an in-app form or Discord channel.

Open testing: Public, but only visible to users who opt in via a link. No app store listing, no reviews yet. This is your canary: monitor crash rates, ANRs, and analytics. If your app bombs here, roll back before thousands are affected in production. Many teams hold open testing for 1 week, monitoring Crashlytics and performance metrics. If crash-free sessions stay above 99.5%, promote to production.

In a ride-hailing scenario, we caught a critical geolocation bug in closed testing that would have bricked the app for drivers in certain regions. Fixing it pre-production saved us from support tickets and a potential delisting.

Set up a rollout strategy in Play Console: start production at 5–10% of users, monitor for 24–48 hours, then ramp to 50%, then 100%. Rollouts let you pull an update if crashes spike, rather than being locked in.

Common Mistakes

  • Mismatched version codes across builds: If you upload a bundle with version code 5, then try to upload an APK with version code 4, Google rejects it. Always increment version code for every upload, even testing tracks.
  • Testing tracks in production release notes: Users see release notes from your internal testing track in their app if you forget to clear them. Embarrassing if your notes say "Test crash fix" or internal jargon.
  • Forgetting to request required permissions in the manifest: If your privacy policy says you request location, but your AndroidManifest.xml doesn't include it, Google's policy checker flags this. Sync your manifest and policy.
  • Uploading from inconsistent machines: Different JDKs, Gradle versions, or NDK versions can produce bundles with subtle differences. Use Docker or a locked CI/CD image for reproducible builds.
  • Not testing in airplane mode or on slow networks: Upload tracking, analytics initialization, and sync operations should degrade gracefully. Test on 3G or with network throttling enabled.

Key Takeaways

  • Enroll in Play App Signing so Google secures your app signing key; manage only your upload key locally.
  • Always upload .aab bundles, not APKs, to save users 15–35% on download size and unlock regional optimization.
  • Invest time in your store listing: concise description, high-quality screenshots, and an honest privacy policy reduce rejection risk and improve install conversion.
  • Use internal, closed, and open testing tracks as quality gates; never ship directly to production.
  • Implement staged rollouts (5% → 50% → 100%) in production to catch issues early before they affect your full user base.
  • Keep version codes monotonically increasing across all tracks and builds to avoid upload conflicts.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles