Android development / Advanced: reliable mobile systems and releases / Lesson 18
Release engineering and observability
Prepare a reproducible release and monitor real user outcomes with privacy-conscious signals and a concrete response plan.
On this page
Lesson goals
- Explain signing, versioning, shrinking, and release verification.
- Design operational signals that connect failures to user impact.
Make the artifact reproducible
A release is a specific source revision, toolchain, dependency set, configuration, and signed artifact. Record those inputs and separate debug endpoints and diagnostics from release behavior. versionCode orders application updates; versionName is a user-facing label and does not replace update ordering. Keep application identity stable. Signing identity determines whether an update can replace an installed app, so losing or changing keys has consequences beyond one build. Use the supported distribution signing workflow and protect upload credentials outside the repository with controlled CI access.
Verify the optimized variant
Code shrinking can remove unreachable code and rename symbols; resource shrinking can remove unused resources. Reflection, serialization, JNI, and dynamically referenced resources may require precise keep rules or supported integration. Avoid a blanket keep-everything rule that hides the problem and defeats optimization. Test the release-like variant because debug success cannot reveal every shrinker issue. Keep mapping files and native symbols associated with the exact artifact so production traces can be interpreted later. These files are operational evidence and should follow an access and retention policy.
android {
buildTypes {
getByName("release") {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}
Merge this supported legacy DSL into app/build.gradle.kts under a compatible Android Gradle plugin. AGP 9.3+ also offers optimization { enable = true } with documented keepRules source sets; follow the DSL chosen by the project. The local rules file must exist. Signing secrets remain external; shrinking alone does not establish release readiness.
Verify the user journeys that can regress
Run appropriate unit, integration, UI, migration, and release-variant checks, then inspect the merged manifest, network policy, exported components, permissions, and backup behavior. Exercise fresh install and upgrade from a supported previous version. Include account switching, denied permissions, offline pending work, process restoration, Arabic layout, and enlarged fonts. Current target-API and store policy requirements change over time, so verify them directly before distribution rather than embedding an alleged latest requirement in a long-lived course. Release readiness is evidence about this artifact and its supported environments.
Measure outcomes without private payloads
Crashes, ANRs, startup latency, frame behavior, queue age, sync failures, and permission-related friction describe different aspects of user experience. Android vitals provides platform and distribution quality signals where available; application telemetry can add domain context. Define event schemas with bounded categories and opaque identifiers instead of titles, notes, tokens, or full URLs. Capture release version and relevant environment dimensions so regressions can be grouped. A metric needs a denominator and sampling explanation: ten failures mean something different among ten operations versus a million operations.
enum class SyncResultCode { SUCCESS, TEMPORARY_FAILURE, CONFLICT, AUTH_REQUIRED }
data class SyncMetric(
val result: SyncResultCode,
val durationMs: Long,
val pendingCount: Int
)
interface MetricsSink { fun record(metric: SyncMetric) }
fun reportSync(sink: MetricsSink, result: SyncResultCode, elapsedMs: Long, pending: Int) {
require(elapsedMs >= 0 && pending >= 0)
sink.record(SyncMetric(result, elapsedMs, pending))
}
Measure durations with a monotonic clock and implement MetricsSink with consent, sampling, retention, and transport policies appropriate to the app. This model deliberately excludes record text and credentials. Validate large or unexpected counts before using them as high-cardinality labels.
Prepare to respond to evidence
A staged rollout limits initial exposure while the team checks agreed quality signals. Define pause criteria, responsible owners, and a recovery plan before publishing. A rollback is not always installing an older versionCode, and a database migration may prevent a simple binary downgrade. Prefer a prepared forward fix or a controlled feature disablement where appropriate. Verify that telemetry itself works in the release configuration and that a reported crash can be symbolicated using the retained mappings. Close the loop by turning an observed regression into a reproducible test and an improved release check.
Common errors and fixes
Only the release variant crashes
- Why it happens
- Shrinking removed dynamically referenced code or release configuration differs.
- How to fix it
- Reproduce with the optimized artifact and add precise supported rules.
- Prevent it next time
- Include release-variant smoke journeys in validation.
Crash traces cannot be interpreted
- Why it happens
- Mappings do not match the distributed artifact.
- How to fix it
- Retrieve and associate the exact version’s mapping or symbols.
- Prevent it next time
- Archive evidence with artifact identity during CI.
A rollback cannot open the upgraded database
- Why it happens
- Binary rollback was planned without schema compatibility.
- How to fix it
- Use a reviewed forward recovery or compatible recovery path.
- Prevent it next time
- Design migration and rollout recovery together.
Check your understanding
1. Debug tests pass, but serialization fails after shrinking. Define the smallest useful investigation and validation.
Show worked solution
Reproduce the failing serialization path in the optimized variant, inspect the library’s official shrinker integration, and add the narrow necessary configuration. Re-run the exact path plus upgrade and startup smoke checks. Avoid keeping the entire application as a first response because it obscures the missing contract.
2. A dashboard reports twenty sync failures after a release. What evidence is needed before deciding to halt rollout?
Show worked solution
Check total operations, affected users, failure categories, queue age, release cohort, and whether failures recover. Compare the previous baseline under similar conditions and inspect representative redacted traces. Use the predefined impact thresholds and data integrity risks, rather than reacting to a raw count without context.
Key takeaways
- Release verification concerns the exact optimized artifact.
- Observability connects technical failures to user impact.
- Recovery planning includes schema compatibility and retained evidence.