Build variants, environments, and secrets
A build type describes how software is built (debug, release). A product flavor represents a product/environment dimension (demo, paid; sometimes staging, production). Their cross-product creates variants, so dimensions must be intentional.
android {
buildTypes {
release { isMinifyEnabled = true }
debug { applicationIdSuffix = ".debug" }
}
flavorDimensions += "environment"
productFlavors {
create("staging") {
dimension = "environment"
applicationIdSuffix = ".staging"
}
create("production") { dimension = "environment" }
}
}
Different application IDs let staging and production coexist. They also imply different OAuth redirect URIs, App Links, Firebase apps, API credentials, content providers, and signing fingerprints—test those contracts.
Secrets
Anything shipped in an APK/AAB is recoverable: BuildConfig, resources, native code, and downloaded config are not secret stores. Public client API identifiers may be shipped but must be restricted by package/certificate and server policy. Private signing keys, service-account credentials, and backend secrets belong in protected CI/server stores.
Inject signing material only into the release job, mask logs, avoid command-line echo, restrict environment approvals, rotate credentials, and never commit a keystore/password file.
Variant control
Variant explosion increases compile/test/release cost. Disable unsupported combinations with the Android Components API and document the supported matrix. Ensure production cannot accidentally point to staging or enable debug diagnostics.
Practical mistakes
- ❌ Treat a staging base URL as the only environment difference.
- ✅ Verify identity, signing, links, analytics, push, billing, and backend policy.
- ❌ Put a “secret” string in
BuildConfig. - ✅ Move trusted secrets server-side.
- ❌ Test only
debugwhile publishing shrunkrelease. - ✅ Add release smoke/contract tests.