Discipline your Android builds to be delivered at any time.

Discipline your Android builds to be delivered at any time.

Strategies and tools used to distribute an android app in beta and prod environments at any time.


Erik Jhordan Rey

August 26, 2020


  1. Erik Jhordan Rey GDE Android @ErikJhordan_Rey Discipline your Android builds

    to be delivered at any time.
  2. Software Delivery

  3. Influenced by Extreme Programming (CI)

  4. Continuous Integration

  5. Practice where members of a team integrate their work frequently,

    usually each person integrates at least daily Continuous Integration
  6. Continuous Delivery

  7. Discipline where you build software in such a way that

    the software can be released to production at any time Continuous Delivery
  8. Continuous Development

  9. Means that every change goes through the pipeline and automatically

    gets put into production Continuous Development
  10. How CI/CD works on Android Apps?

  11. Distribution Challenges Faced by Android Developers

  12. Distribution Challenges • The team lead generate the build manually

    on their machine • One member of the team has the keys to signing the app • Attend issues and make Hotfixes easier • APK is shared on slack, drive, dropbox, email or another. • Connect a QA's device to your personal computer to someone can test a version (apk or bundle) • The wrong signed app builds (third-party libs keys) • Nobody knows the features included in the release • Impossible to show premature versions • Google Play Internal test can not be used instantaneously • Firebase Distribution does not support app bundles
  13. Automate Android builds matters

  14. Why automate the app release matters? • Satisfy the customer

    through early and continuous delivery of valuable software. • Visibility to all team about the development • Fix broken builds Immediately • Make it easy for anyone to get the latest version • Deliver working software frequently • A standardized way to make releases
  15. Android Delivery Guide

  16. “This is a guide on which tools and strategies, I

    have implemented in real-world projects. Not rules or silver bullet tools!”
  17. Proprietary + Confidential Android Delivery Guide Workflow Reduced Risk Source:

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non erat sem Distribution
  18. Workflow

  19. “A great development workflow will make your good developers great,

    and your great developers exceptional. A bad workflow will take your best engineers' productivity down to level and make wish he worked elsewhere.”
  20. Contributor Guide

  21. Contributor Guide Tools to d Define a contributor guide of

    integration code, where developers can read, guidelines, styles, tools, and resources to start to collaborate with the project. Branches • Our main branch is master • We work on feature branches and do PRs to merge them to master. • We used a release branch to prepare the Release Candidates, HotFix and finally do the Release.
  22. Define Build Types

  23. Define Build Types Tools to d Design strategies to build

    different versions of the app to test different environments Builds Debug: Build Feature Integration with development rules, logging tools, and available test in a staging environment used by the development team. Preview: Build Candidate with production rules without logs tools, but available test in production environment used by the Internal team (All Team) Production: Build Production to be uploaded to Google Play Note: If you're using flavors, consider the different combinations
  24. Preview Validate Build Deploy Workflow Prod Debug Pull Requests CI

    Job merge to master release branch Tag Release Google Play Firebase App Distribution
  25. Reduced Risk

  26. Continuous Integration Tools

  27. CI Tools Tools to d

  28. CI setup Tools to d Each integration is verified by

    an automated build to detect integration errors as quickly as possible • Specify branches a. master b. "release-0.1.0" - /^release-\d+\.\d+\.\d+$/ c. "0.1.0" - /^\d+\.\d+\.\d+$/ • Keep the build fast • Try different configurations until finding the most performed to your project
  29. Assemble Builds

  30. Assemble Builds Tools to d A key part of doing

    a continuous build is that if the mainline build fails, it needs to be fixed right away. • Prevent merge broken PR's • Easier back to previous code versions • You're always developing on a known stable code base
  31. APK Assemble main outputs for all the variants ./gradlew assemble

    ./gradlew assembleDebug ./gradlew assemblePreview ./gradlew assembleProd ----------------------------- Dynamic Features - BundleToStandaloneApkTask ./gradlew packageDebugUniversalApk
  32. Code Analysis Tools

  33. Code Analysis Tools Tools to d Analyze your source code

    to find potential vulnerabilities such as bugs and ensure conformance to coding guidelines. Keep your code healthy and maintain code quality. • Android Lint: Inspect & Validate Java, Kotlin & XML • detekt: Highly configurable analysis tool for your Kotlin projects (code smells & complexity) • ktlint: Kotlin linter based on kotlinlang.org rules and code formatter • PMD: Multi analyzer code for Java, Xml & more • FindBugs: Detects possible bugs in Java • checkstyle: Check for naming conventions, structural issues, class design for Java code
  34. Run all Checks ./gradlew check ./gradlew checkstyle ./gradlew detekt Travis

    CI - Checks script: - ./gradlew check assemble
  35. Testing Pipeline

  36. Testing Pipeline Tools to d Make your build self testing

    catch bugs more quickly and efficiently is to include automated tests in the build process. • Testing the business logic • Testing the integration with a server API’s • Testing User Interface • Testing Persistence Integration
  37. Run Tests ./gradlew test ./gradlew connectedAndroidTest The Gradle check task

    depends on the test task which means test is executed before check is run. ./gradlew check
  38. Connect Device Farm

  39. Device Farm Tools to d

  40. Distribution

  41. App Signing automatically Create a file inside the project to

    store the signing keys debug.properties APP_DEBUG_STORE_PASSWORD=YourStorePassword APP_DEBUG_KEY_ALIAS=YourAlias APP_DEBUG_KEY_PASSWORD=YourkeyPassword
  42. signing.gradle ext { signingDebug = { def propertiesFile = loadPropertiesFile("debug")

    android.signingConfigs.debug { storeFile loadFileFromGoogleDir("debug.keystore") if (propertiesFile.exists()) { def properties = loadProperties(propertiesFile) storePassword properties.APP_DEBUG_STORE_PASSWORD keyAlias properties.APP_DEBUG_KEY_ALIAS keyPassword properties.APP_DEBUG_KEY_PASSWORD } else { storePassword System.getenv("APP_DEBUG_STORE_PASSWORD") keyAlias System.getenv("APP_DEBUG_KEY_ALIAS") keyPassword System.getenv("APP_DEBUG_KEY_PASSWORD") } } } https://erikjhordan-rey.github.io/blog/2020/03/15/ANDROID-launch-android-apps.html
  43. apply plugin: 'com.android.application' //... apply from: rootProject.file("gradle-scripts/google/signing.gradle") signingConfigs { debug

    { signingDebug() } preview { signingPreview() } prod { signingProd() } }
  44. Version Properties Increase the version on master ( increasing the

    MAJOR, MINOR or PATCH, ie. move to 1.5.0 version.properties MAJOR=1 MINOR=5 PATCH=0
  45. Version Code & Name Generate automatically the version code and

    name app-distribution.gradle ext { versionMajor = getVersionMajor() versionMinor = getVersionMinor() versionPatch = getVersionPatch() version_name = "${versionMajor}.${versionMinor}.${versionPatch}" version_code = versionMajor * 10000 + versionMinor * 100 + versionPatch version_date = generateBuildTime() }
  46. version.properties private def getVersionMajor() { Properties properties = getVersionProperties() return

    properties['MAJOR'].toInteger() } private def getVersionMinor() { Properties properties = getVersionProperties() return properties['MINOR'].toInteger() } private def getVersionPatch() { Properties properties = getVersionProperties() return properties['PATCH'].toInteger() } private def getVersionProperties() { def content = rootProject.file("gradle-scripts/distribution/version.properties") Properties properties = new Properties() InputStream is = new ByteArrayInputStream(content.getBytes()) properties.load(is) return properties }
  47. Single Module apply plugin: 'com.android.application' //... apply from: rootProject.file("gradle-scripts/distribution/app-distribution.gradle") Multi-module

    subprojects { apply from: rootProject.file("gradle-scripts/distribution/app-distribution.gradle") }
  48. android.gradle defaultConfig { ... versionCode version_code versionName version_name } Android

    Module Config - com.android.library apply from: rootProject.file("gradle-scripts/shared-config/android.gradle") Dynamic Feature Config - com.android.dynamic-feature apply from: rootProject.file("gradle-scripts/shared-config/dynamic-feature.gradle") Shared Configuration Module A Module B Module C
  49. Internal Release

  50. Firebase Test Distribution

  51. Internal Distribution Write Code Push Code Run Tests & Checks

    on CI Distribution QA Team Debug Preview
  52. Firebase App Distribution apply plugin: 'com.android.application' // ... apply plugin:

    'com.google.firebase.appdistribution' firebaseAppDistribution { releaseNotesFile = "path/release-notes.txt" groups = "internal-testers" serviceCredentialsFile = rootProject.file("gradle-scripts/distribution/service-account.json") }
  53. Firebase App Distribution + Debug Build Type debug { def

    type = "debug" applicationIdSuffix ".${type}" versionNameSuffix "-${type}" .... signingConfig signingConfigs.debug firebaseAppDistribution { releaseNotesFile = "./release-notes.txt" groups = "internal-testers" serviceCredentialsFile = rootProject.file("gradle-scripts/distribution/service-account.json") } }
  54. Firebase App Distribution + Preview Build Type preview { def

    type = "preview" applicationIdSuffix ".${type}" versionNameSuffix "-${type}" .... ... firebaseAppDistribution { releaseNotesFile = "gradle-scripts/distribution/release_notes.txt" groups = "internal-testers" serviceCredentialsFile = rootProject.file("gradle-scripts/distribution/service-account.json") } }
  55. Debug + Publishing Universal APK ./gradlew appDistributionUploadDebug Travis CI -

    master deploy: - provider: script script: ./gradlew appDistributionUploadDebug on: branch: master
  56. Preview + Publishing Universal APK ./gradlew appDistributionUploadPreview Travis CI -

    Release Branch - release-1.5.0 deploy: - provider: script script: ./gradlew appDistributionUploadPreview on: all_branches: true condition: $TRAVIS_BRANCH =~ ^release-.*$
  57. Communication Plan

  58. Communication Plan Tools to d Each new release impacts the

    whole team and company, not just the mobile developers, we need to communicate the most relevant changes included in the app. • Debug: Auto Generate release notes with last commit message • Preview: Add release notes with the most relevant changes • Production: Every ticket in JIRA linked to a merged PR should set the field “Fix Version/s:” to the next Release. This will help to generate automatically the changelog at the end of each release
  59. Generate Release Notes Automatically task generateReleaseNotes { doLast({ def releaseNotes

    = new File('release-notes.txt') releaseNotes.delete() releaseNotes << "Release Notes - Version $name_version (${date_version})\n\n" releaseNotes << "Change Log \n\n" def lastCommitCommand = "git log --format=%s --no-merges -n 1" def commitMessageLines = lastCommitCommand.execute() commitMessageLines.in.eachLine { line -> releaseNotes << "* " + line + "\n\n\n" } releaseNotes << "This file was automatically generated." }) } ./gradlew generateReleaseNotes
  60. Firebase App Distribution Release Notes When a PR is merged

    to master release notes is generated automatically with the latest commit
  61. Quality Assurance Team

  62. QA Team Tools to d Things always go wrong in

    production, but this does not have to be a bad thing. It’s an opportunity to learn about your system and the real world with which it interacts. Product: Ensure a product team test the requirements business logic QA: Help you gain a richer understanding of the real issues your system faces (Test automation means that you are more empowered in your role than ever.)
  63. Dynamic URL Environment

  64. Dynamic URL Environment Tools to d Implement a hidden feature

    (Dynamic URL) to your team can test an app pointing to different URL Environments without a new release. App Builds Debug: Set a specific URL environment with a development build Preview: Set a specific URL environment with a pre-production build
  65. Production Release

  66. Open Alpha, Beta & Prod

  67. External Distribution Write Code Push Code Run Tests & Checks

    on CI Distribution Prod
  68. Gradle Play Publisher classpath "com.github.triplet.gradle:play-publisher:2.7.2" ... apply plugin: "com.github.triplet.play" ...

    play { serviceAccountCredentials = rootProject.file("gradle-scripts/distribution/service-account.json") track = "internal" userFraction = 0.5 releaseStatus = "inProgress" }
  69. Publishing APKs ./gradlew publishApk Publishing Bundles ./gradlew publishReleaseBundle Travis CI

    - Tag - 1.5.0 deploy: - provider: script script: ./gradlew publishReleaseBundle on: tags: true
  70. Staged Rollouts

  71. Staged Rollout Tools to d With a staged rollout, your

    update reaches only a percentage of your users, which you can increase over time. • New and existing users are eligible to receive updates from staged rollouts and are chosen at random for each new release rollout. • During a staged rollout, it's a good idea to closely monitor crash reports and user feedback. • Increase your staged rollout percentage according to the number of user installs
  72. What about crashes in Production?

  73. Monitoring App In Production

  74. Monitoring Your App Tools to d Gathering operational data about

    a system is common practice, particularly metrics that indicate app behavior as performance, memory usage, crash-free user, daily active users, analytics data, etc • Use Firebase Crashlytics is a lightweight, realtime crash reporter that helps you track, prioritize, and fix stability issues that erode your app quality. • Integrate used services to be notified as soon as possible ej. (Slack + Firebase Crashlytics ) • Verify performance and analytics data
  75. Feature Toggles

  76. Feature Toggles Tools to d Is a set of patterns

    which can help us to deliver new functionality to users rapidly but safely without a new release • Use Firebase Remote Config is a cloud service that lets you change the behavior and appearance of your app without requiring users to download an app update • Create your own platform or system to turn on/off features
  77. Remote Config Tools to d

  78. 11 Weeks of Android: App distribution and monetization on Google

    Play Tools to d https://android-developers.googleblog.com/2020/08/11-weeks-of-android-app-distribution.html https://developer.android.com/distribute/play-policies
  79. Automation tend to have dramatically less bugs, both in production

    and in process
  80. Recommendations • Not needed to have a complex workflow to

    implement CI/CD, start with minimum checks and validations • Try different setups in order to find the correct or fits your needs • Considerate company's resources and the size team at the moment choose a CI tool • Use kotlin DSL if you feel more comfortable to avoid Gradle files. • Keep the builds fast • Satisfy the customer through early and continuous delivery of valuable software. ¡Automate all the things!
  81. shorturl.at/ampR5

  82. Further reading

  83. Further Reading • https://ronjeffries.com/xprog/what-is-extreme-programming/#whole • https://martinfowler.com/articles/continuousIntegration.html • https://martinfowler.com/bliki/ContinuousDelivery.html • https://martinfowler.com/articles/qa-in-production.html

    • https://firebase.google.com/docs/app-distribution • https://github.com/Triple-T/gradle-play-publisher • https://android-developers.googleblog.com/2020/08/11-weeks-of-android-app-distribu tion.html • https://speakerdeck.com/erikjhordan_rey/pragmatic-mobile-testing-strategies • https://erikjhordan-rey.github.io/blog/2020/03/15/ANDROID-launch-android-apps.html • https://anchor.fm/erik-jhordan-rey
  84. Erik Jhordan Rey GDE Android @ErikJhordan_Rey Thank You!