Slide 1

Slide 1 text

HEALTH TRACKING APP FIGHTING THE TABOO TOGETHER

Slide 2

Slide 2 text

CAN YOU READ MY CODE? DROIDCON KRAKOW 2017

Slide 3

Slide 3 text

CODE IS READ MORE THAN WRITTEN WHY?

Slide 4

Slide 4 text

GROWING TEAMS WHY?

Slide 5

Slide 5 text

YOU AND GOD REALITY CHECK

Slide 6

Slide 6 text

COMMENTS

Slide 7

Slide 7 text

WHAT TO COMMENT?

Slide 8

Slide 8 text

COMMENTS COMMENT THE WHY NOT THE WHAT "foo() looks like this because it turned out all the other 15 solutions we tried sucked”

Slide 9

Slide 9 text

COMMENTS COMMENT MAGIC CONSTANTS val NUM_THREAD = 8 val MAGIC_CONSTANT = 42

Slide 10

Slide 10 text

COMMENTS COMMENT MAGIC CONSTANTS val NUM_THREAD = 8 // My desk legs * 2 val MAGIC_CONSTANT = 42 // The Answer

Slide 11

Slide 11 text

COMMENTS USE COMMON MARKERS // TODO “We should [add random task] in the future” // HACK “This works but is kinda hacky” // HERE BE DRAGONS

Slide 12

Slide 12 text

COMMENTS USE YOUR OWN ANNOTATIONS @TechDebt(jira = "https://biowink.atlassian.net/browse/ATD-28") class BackupActivity : BaseActivity() { … } /** * The [TechDebt] annotation can be used… */ annotation class TechDebt(val jira: String)

Slide 13

Slide 13 text

COMMENTS DO NOT COMMENT ▸ Constructors, getters, setters… Seriously! ▸ Bad functions names. Fix them! ▸ Everything that can be inferred from the context and the code.

Slide 14

Slide 14 text

COMMENTS FROM YOUR HEART TO YOUR CODE What you actually mean and should write: “Careful! This routine is not thread-safe!” What you might write while panicking or thinking you are funny or helpful: “Sh*t! This thing will blow up the moment 
 we start using threads! Fingers crossed!”

Slide 15

Slide 15 text

COMMENTS FROM YOUR HEAD TO YOUR CODE scrollsSubscription = calendar .scrollEvents() .map { it.scrollState() } .filter { it == OnScrollListener.SCROLL_STATE_IDLE } .skip(5) .subscribe( { scrollsCount++ }, { Timber.e(it, "Scroll events observable died ") } )

Slide 16

Slide 16 text

COMMENTS FROM YOUR HEAD TO YOUR CODE /* To be able to effectively track scroll events we start observing every scroll event we focus on the scroll state we ignore everything that's not [OnScrollListener.SCROLL_STATE_IDLE] we skip the first 5 events because Android fires 5 bogus events on screen loading we increment the counter that we will later use when the user navigates away from the screen. */ scrollsSubscription = calendar .scrollEvents() .map { it.scrollState() } .filter { it == OnScrollListener.SCROLL_STATE_IDLE } .skip(5) .subscribe( { scrollsCount++ }, { Timber.e(it, "Scroll events observable died ") } )

Slide 17

Slide 17 text

COMMENTS FROM YOUR HEAD TO YOUR CODE interface AdsManager { fun hasPrefetchedAd(placement: Placement): Boolean }

Slide 18

Slide 18 text

COMMENTS FROM YOUR HEAD TO YOUR CODE interface AdsManager { /** * This method checks if there is a prefetched ad for the given [placement]. * * @param placement the placement for which to check if a prefetched ad is * available * @return true if a prefetched ad for the given placement is available, * false otherwise */ fun hasPrefetchedAd(placement: Placement): Boolean }

Slide 19

Slide 19 text

VARIABLES

Slide 20

Slide 20 text

VARIABLES THEY DON’T DESERVE A NAME tmp retval i, k, j foo

Slide 21

Slide 21 text

VARIABLES PACK INFORMATION IN THE NAME ▸Is being temporary the only actual quality of tmp? ▸If retval is a list of employees that we are returning, why don’t we call it employees? ▸Can those loop indexes improve the general readability somehow?

Slide 22

Slide 22 text

VARIABLES LOOP INDEXES for members[j] for clubs[i] for towns[k] doSomething()

Slide 23

Slide 23 text

VARIABLES LOOP INDEXES for members[members_i] for clubs[clubs_i] for towns[towns_i] doSomething()

Slide 24

Slide 24 text

VARIABLES LOOP INDEXES for members[i] for clubs[j] for towns[k] doSomething()

Slide 25

Slide 25 text

VARIABLES LOOP INDEXES members[i] clubs[j] towns[k] doSomething() members[j] clubs[i] towns[k] doSomething()

Slide 26

Slide 26 text

VARIABLES LOOP INDEXES members[clubs_i] clubs[towns_i] towns[members_i] doSomething()

Slide 27

Slide 27 text

VARIABLES HELPERS VARIABLES if (pair.left.split('@')[0] == "root") {...} //

Slide 28

Slide 28 text

VARIABLES HELPERS VARIABLES if (pair.left.split('@')[0] == "root") {...} // HOW ABOUT THIS?

Slide 29

Slide 29 text

VARIABLES HELPERS VARIABLES val email = pair.left // TODO [CLUE-1234] Migrate to data class

Slide 30

Slide 30 text

VARIABLES HELPERS VARIABLES val username = email.split('@')[0] //

Slide 31

Slide 31 text

VARIABLES HELPERS VARIABLES fun Email.toUsername() = split(‘@')[0] // val username = email.toUsername() //

Slide 32

Slide 32 text

VARIABLES HELPERS VARIABLES val email = … val username = email.toUsername() if (username == “root”) { … }

Slide 33

Slide 33 text

VARIABLES HELPERS VARIABLES if(request.userId == document.userId) { // […] } else { […] }

Slide 34

Slide 34 text

VARIABLES HELPERS VARIABLES val userOwnsDocument = (request.userId == document.userId) if (userOwnsDocument) […] else […] //

Slide 35

Slide 35 text

VARIABLES HELPERS VARIABLES premiumManager.isFeatureUnlocked( country, feature.type, premiumManager.getActiveProducts( getIdsForActiveProducts(), configuration ), configuration )

Slide 36

Slide 36 text

VARIABLES HELPERS VARIABLES val activeProducts = premiumManager.getActiveProducts( getIdsForActiveProducts(), configuration) premiumManager.isFeatureUnlocked( country, feature.type, activeProducts, configuration)

Slide 37

Slide 37 text

BOOLEANS PACK INFORMATION IN NAMES val readPassword = true

Slide 38

Slide 38 text

BOOLEANS PACK INFORMATION IN NAMES val needsPassword = true val userIsAuthenticated = true

Slide 39

Slide 39 text

BOOLEANS PACK INFORMATION IN NAMES val disableSSL = false // val useSSL = true // ❤

Slide 40

Slide 40 text

FUNCTIONS

Slide 41

Slide 41 text

FUNCTIONS PACK INFORMATION IN THE SIGNATURE fun delay(amount: Int) //

Slide 42

Slide 42 text

FUNCTIONS PACK INFORMATION IN THE SIGNATURE fun delay(amount_seconds: Int) //

Slide 43

Slide 43 text

FUNCTIONS PACK INFORMATION IN THE SIGNATURE typealias Seconds = Int fun delay(amount: Seconds) // ❤

Slide 44

Slide 44 text

FUNCTIONS PACK INFORMATION IN THE SIGNATURE fun createCache(size: Int) //

Slide 45

Slide 45 text

FUNCTIONS PACK INFORMATION IN THE SIGNATURE typealias Megabytes = Int fun createCache(size: Megabytes) // ❤

Slide 46

Slide 46 text

FUNCTIONS SIDE NOTES ‣ Aim for small reusable functions ‣ Aim for pure functions 1. Dependencies IN 2. Result OUT 3. No side effects

Slide 47

Slide 47 text

IF THEN WHAT? THE ORDER if (length >= 10) {…} if (10 <= length) {…}

Slide 48

Slide 48 text

IF THEN WHAT? THE ORDER ‣ Left: the expression being interrogated, the one more likely to change ‣ Right: the expression being compared against, the one that stays the same over time

Slide 49

Slide 49 text

IF THEN WHAT? THE ORDER ‣ Positive case first ‣ Simpler case first ‣ More interesting case first ‣ The case that returns earlier first

Slide 50

Slide 50 text

IF THEN WHAT? TERNARY OPERATOR Given that lines are free When one single line can be replaced with four lines of more easy-to-understand code Then don't be a j*rk! ⚠

Slide 51

Slide 51 text

IF THEN WHAT? TERNARY OPERATOR fun isUserOfAge(user: User?) = user?.age.takeIf { it >= 18 } ?: false

Slide 52

Slide 52 text

IF THEN WHAT? TERNARY OPERATOR fun isUserOfAge(user: User?) = if (user == null) false else user.age >= 18

Slide 53

Slide 53 text

THE USER PROFILE REAL WORLD EXAMPLE

Slide 54

Slide 54 text

THE USER PROFILE USER OBJECT data class User( name: String, lastName: String, email: String)

Slide 55

Slide 55 text

OBTAINING THE USER PROFILE fun getUser() : User THE USER PROFILE

Slide 56

Slide 56 text

fun fetchUser() : User THE USER PROFILE OBTAINING THE USER PROFILE

Slide 57

Slide 57 text

fun downloadUser() : User THE USER PROFILE OBTAINING THE USER PROFILE

Slide 58

Slide 58 text

fun loadUser() : User THE USER PROFILE OBTAINING THE USER PROFILE

Slide 59

Slide 59 text

THE USER PROFILE OBTAINING THE USER PROFILE fun loadUser(callback: Callback) fun loadUser(): Observable

Slide 60

Slide 60 text

THE USER PROFILE OBTAINING THE USER PROFILE fun loadUser(): Observable loadUser() .subscribeOn(Schedulers.io()) […]

Slide 61

Slide 61 text

THE USER PROFILE OBTAINING THE USER PROFILE fun loadUser(): Single loadUser() .subscribeOn(Schedulers.io()) […]

Slide 62

Slide 62 text

NAMING IS HARD YES

Slide 63

Slide 63 text

WRAPPING UP HEALTHY DEV TEAM TIPS ‣ YAGNI and KISS ‣ Keep your codebase small ‣ Create a routine of removing bad code ‣ Keep writing and maintaining your tests

Slide 64

Slide 64 text

WRAPPING UP FURTHER READINGS ‣ The Art of Readable Code ‣ Working effectively with legacy code ‣ The pragmatic programmer ‣ Mini Habits: Smaller Habits, Bigger Results

Slide 65

Slide 65 text

BE BRAVE Ivan Morgillo @hamen Sebastiano Gottardo @rotxed