Slide 1

Slide 1 text

Memories of a remote and lonely Android developer

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

When considering a new job, 53.3% said remote options were a top priority

Slide 5

Slide 5 text

A majority of developers 63.9% reported working remotely at least one day a month

Slide 6

Slide 6 text

11.1% say they’re full-time remote or almost all the time

Slide 7

Slide 7 text

http://stackoverflow.com/insights/survey/2017

Slide 8

Slide 8 text

http://blog.ovidiubokar.com/sites/default/files/images/dilbert-working-from-home.jpg

Slide 9

Slide 9 text

What does remote mean?

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

A bad writer could turn a 2-minute chat into a 10-minute chat “ - Nick Francis CEO Help Scout

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

Hours 0 7.5 15 22.5 30 Android Studio Slack Github.com Inbox twitter.com facebook.com 1 2.5 3 4 8 22 1 3 2 3 6 25 March February

Slide 20

Slide 20 text

Lonely

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

Your main goal is to ship a product

Slide 23

Slide 23 text

Your main goal is to ship a product

Slide 24

Slide 24 text

Productive

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

7 stages of naming

Slide 27

Slide 27 text

helpscout api app database

Slide 28

Slide 28 text

app common data domain injection

Slide 29

Slide 29 text

domain conversations folders mailboxes session search settings

Slide 30

Slide 30 text

mailboxes model usecases view MailboxesMVP MailboxesPresenter

Slide 31

Slide 31 text

Static analysis

Slide 32

Slide 32 text

Static analysis apply plugin: 'com.android.application' // wrapper task to run all static code analysis task staticAnalysis { description 'Runs checkstyle, findbugs and pmd against the main sources.' group 'Verification' } def sourceSetMain = android.sourceSets.main.java.srcDirs def rulesDir = new File(project.teamPropsDir, 'rules') def ignoreFail = !project.failFastOnError def includeSrcPatternApplication = 'net/helpscout/**/*.java' def excludeSrcPatternClass = 'net/helpscout/android/**/R.java' def excludeSrcPatternGeneratedFiles = '**/gen/**' def excludeSrcDagger = 'net/helpscout/android/injection*.java'

Slide 33

Slide 33 text

Static analysis apply plugin: 'com.android.application' // wrapper task to run all static code analysis task staticAnalysis { description 'Runs checkstyle, findbugs and pmd against the main sources.' group 'Verification' } def sourceSetMain = android.sourceSets.main.java.srcDirs def rulesDir = new File(project.teamPropsDir, 'rules') def ignoreFail = !project.failFastOnError def includeSrcPatternApplication = 'net/helpscout/**/*.java' def excludeSrcPatternClass = 'net/helpscout/android/**/R.java' def excludeSrcPatternGeneratedFiles = '**/gen/**' def excludeSrcDagger = 'net/helpscout/android/injection*.java'

Slide 34

Slide 34 text

Static analysis apply plugin: 'com.android.application' // wrapper task to run all static code analysis task staticAnalysis { description 'Runs checkstyle, findbugs and pmd against the main sources.' group 'Verification' } def sourceSetMain = android.sourceSets.main.java.srcDirs def rulesDir = new File(project.teamPropsDir, 'rules') def ignoreFail = !project.failFastOnError def includeSrcPatternApplication = 'net/helpscout/**/*.java' def excludeSrcPatternClass = 'net/helpscout/android/**/R.java' def excludeSrcPatternGeneratedFiles = '**/gen/**' def excludeSrcDagger = 'net/helpscout/android/injection*.java'

Slide 35

Slide 35 text

Static analysis apply plugin: 'checkstyle' task checkstyleMain(type: Checkstyle) { ... } staticAnalysis.dependsOn checkstyleMain task verifyNoCheckstyleWarnings { doLast { File warningsFile = file(‘buildMessage/reports/ checkstyle/main.xml') if (warningsFile.exists() && warningsFile.text.contains("

Slide 36

Slide 36 text

Static analysis apply plugin: 'checkstyle' task checkstyleMain(type: Checkstyle) { ... } staticAnalysis.dependsOn checkstyleMain task verifyNoCheckstyleWarnings { doLast { File warningsFile = file(‘buildMessage/reports/ checkstyle/main.xml') if (warningsFile.exists() && warningsFile.text.contains("

Slide 37

Slide 37 text

Static analysis apply plugin: 'checkstyle' task checkstyleMain(type: Checkstyle) { ... } staticAnalysis.dependsOn checkstyleMain task verifyNoCheckstyleWarnings { doLast { File warningsFile = file(‘buildMessage/reports/ checkstyle/main.xml') if (warningsFile.exists() && warningsFile.text.contains("

Slide 38

Slide 38 text

The process

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

Who is going to review my code?

Slide 42

Slide 42 text

Who is going to review my code?

Slide 43

Slide 43 text

Page heading Open Pull Request !rst, the old way

Slide 44

Slide 44 text

Pull Requests now

Slide 45

Slide 45 text

Pull Requests now

Slide 46

Slide 46 text

The planning

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

No content

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

Github integration in Android Studio

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

No content

Slide 55

Slide 55 text

Release process

Slide 56

Slide 56 text

The pipeline

Slide 57

Slide 57 text

Amazon Device Farm and Jenkins

Slide 58

Slide 58 text

Travis before_install: - chmod +x team-props/firebase/firebase_tests.sh - chmod +x team-props/firebase/proguard_mapping.sh - chmod +x team-props/release/release.sh script: - ./gradlew clean build - team-props/firebase/firebase_tests.sh - team-props/release/release.sh - team-props/firebase/proguard_mapping.sh

Slide 59

Slide 59 text

Firebase Test Lab #!/bin/bash # Use gcloud from Google to run Espresso tests # on Firebase Test Lab BRANCH='master'; ACCOUNT='your.iam.gserviceaccount.com'; FILE='./team-props/firebase/google_cloud_secret.json'; if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then echo "Skipping tests on Firebase: was pull request." elif [ "$TRAVIS_BRANCH" != "$BRANCH" ]; then echo "Skipping tests on Firebase: wrong branch. Expected '$BRANCH' but was '$TRAVIS_BRANCH'." else echo "Starting tests on Firebase" https://firebase.google.com/docs/test-lab/command-line

Slide 60

Slide 60 text

Firebase Test Lab https://firebase.google.com/docs/test-lab/command-line ./google-cloud-sdk/bin/gcloud auth activate-service- account $ACCOUNT --key-file $FILE ./google-cloud-sdk/bin/gcloud beta test android run -- async --type instrumentation --app ./app/build/outputs/ apk/app-debug.apk --test ./app/build/outputs/apk/app- debug-androidTest.apk --device-ids Nexus5 --os-version-ids 22 --project help-scout

Slide 61

Slide 61 text

Publish to Alpha / Beta apply plugin: 'com.github.triplet.play' def playstoreServiceAccount = System.env.PLAYSTORE_SERVICE_ACCOUNT def playstorePk12File = System.env.PLAYSTORE_PK12_FILE if (playstoreServiceAccount && playstorePk12File) { play { track = 'beta' serviceAccountEmail = playstoreServiceAccount pk12File = rootProject.file(playstorePk12File) } } project.afterEvaluate { def publishApkRelease = project.tasks.getByName("publishApkRelease") def incrementVersionForRelease = project.tasks.getByName("incrementVersionForRelease") publishApkRelease.dependsOn incrementVersionForRelease

Slide 62

Slide 62 text

Publish to Alpha / Beta #!/bin/bash # Use Google Play plugin to release an app version to the Alpha Channel BRANCH='master'; GRADLE_TASK='publishProductionRelease'; if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then echo "Skipping release on Google Play: was pull request." elif [ "$TRAVIS_BRANCH" != "$BRANCH" ]; then echo "Skipping release on Google Play: wrong branch. Expected '$BRANCH' but was '$TRAVIS_BRANCH'." else echo "Starting release on Google Play" ./gradlew $GRADLE_TASK echo "APK upload to Google Play complete" fi

Slide 63

Slide 63 text

Publish to Alpha / Beta

Slide 64

Slide 64 text

Publish to Alpha / Beta

Slide 65

Slide 65 text

Thank you! David González @dggonzalez [email protected]

Slide 66

Slide 66 text

Remote: Office not required (book) - https://goo.gl/yAZOAu 1 Resources What We’ve Learned Building a Remote Culture - https://www.helpscout.net/blog/remote-culture/ Why I Stopped Using Multiple Monitors - https://hackernoon.com/why-i-stopped-using-multiple-monitors-bfd87efa2e5b The source of all technical debt - https://speakerdeck.com/malmstein/the-source-of-all-technical-debt Good naming is a process, not a single step - http://arlobelshee.com/good-naming-is-a-process-not-a-single-step/ Stack Overflow Developer Survey 2017 - http://stackoverflow.com/insights/survey/2017