Slide 1

Slide 1 text

Clean Code with Kotlin

Slide 2

Slide 2 text

What is Clean Code? “Express your intentions clearly to the reader of the code. Unreadable code isn’t clever.” Venkat Subramaniam

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

MEANINGFUL NAMES

Slide 6

Slide 6 text

A name should reveal intent and should answer at these questions: ● Why it exists? ● What it does? ● How it is used? Use intention-revealing names

Slide 7

Slide 7 text

Examples Types Names Classes and Objects , , Methods Solution domain names Problem domain names

Slide 8

Slide 8 text

“UNCLEAN” CODE CLEAN CODE

Slide 9

Slide 9 text

if (t % 5 == 0) { val w = t / 5 println("$w week(s)") } else { println("$t days") } TODO: display the number of days/weeks until an offer will expire

Slide 10

Slide 10 text

if (daysUntilOfferEnds % numberOfDaysInAWeek == 0) { val numberOfWeeks = daysUntilOfferEnds / numberOfDaysInAWeek println("$numberOfWeeks week(s)") } else { println("$daysUntilOfferEnds days") } TODO: display the number of days/weeks until an offer will expire

Slide 11

Slide 11 text

class Address { var addressCountry: String? = null var addressCity: String? = null var addressStreety: String? = null var addressStreetNo: Int = 0 } TODO: define a class for geo addresses

Slide 12

Slide 12 text

class Address { var country: String? = null var city: String? = null var street: String? = null var streetNumber: Int = 0 } TODO: define a class for geo addresses

Slide 13

Slide 13 text

users.filter{ it.job == Job.Developer } .map{ it.birthDate.dayOfMonth } .filter{ it <= 10 } .min() Warning: Use explicit argument names and avoid using too often “it”

Slide 14

Slide 14 text

users.filter{ user -> user.job == Job.Developer } .map{ developer -> developer.birthDate.dayOfMonth } .filter { birthDay -> birthDay <= 10 } .min() Warning: Use explicit argument names and avoid using too often “it”

Slide 15

Slide 15 text

Say YES to immutability ● Prefer to use val over var ● Prefer to use read-only properties over mutable properties ● Prefer to use read-only collections over mutable collections ● Prefer to use data classes that give us copy()

Slide 16

Slide 16 text

FUNCTIONS

Slide 17

Slide 17 text

Functions are the verbs of the language, and classes are the nouns.

Slide 18

Slide 18 text

Basic Rules: ➔ The functions should be small ➔ The functions should be smaller than that Make it smaller

Slide 19

Slide 19 text

Extra Rules: ➔ No many arguments ➔ No side effects ➔ Indent level max 2 Make it smaller

Slide 20

Slide 20 text

fun parseProduct(response: Response?): Product? { if (response == null) { throw ClientException("Response is null") } val code: Int = response.code() if (code == 200 || code == 201) { return mapToDTO(response.body()) } if (code >= 400 && code <= 499) { throw ClientException("Invalid request") } if (code >= 500 && code <= 599) { throw ClientException("Server error") } throw ClientException("Error $code") }

Slide 21

Slide 21 text

fun parseProduct(response: Response?) = when (response?.code()){ null -> throw ClientException("Response is null") 200, 201 -> mapToDTO(response.body()) in 400..499 -> throw ClientException("Invalid request") in 500..599 -> throw ClientException("Server error") else -> throw ClientException("Error ${response.code()}") }

Slide 22

Slide 22 text

for (user in users) { if(user.subscriptions != null) { if (user.subscriptions.size > 0) { var isYoungerThan30 = user.isYoungerThan30() if (isYoungerThan30) { countUsers++ } } } } WARNING: keep the abstraction level consistent and avoid nested code

Slide 23

Slide 23 text

var countUsersYoungerThan30WithSubscriptions = 0 for (user in users) { if (user.isYoungerThan30WithSubscriptions) { countUsersYoungerThan30WithSubscriptions++; } } WARNING: keep the abstraction level consistent and avoid nested code

Slide 24

Slide 24 text

Command Query Separation Command Query Output System System System Changed state Output (result) Changed state Output (result)

Slide 25

Slide 25 text

override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putString(KEY_USER_NAME, username) } Command

Slide 26

Slide 26 text

fun getProfileInfo(account: SignInAccount): UserProfile { var userProfile = UserProfile() if (account != null) { userProfile.name = account.displayName userProfile.email = account.email userProfile.id = account.id } return userProfile } Query

Slide 27

Slide 27 text

CLASSES Dog Cat Baby Dog Pet

Slide 28

Slide 28 text

Class organization ● A class should be smaller than small. ● It should have only one responsibility and a single reason to change. ● A class collaborates with a few others to achieve the desired system behaviors.

Slide 29

Slide 29 text

The newspaper metaphor

Slide 30

Slide 30 text

Cohesion ● Cohesion is a measure of the degree to which the elements of the class/ module are functionally related. ● When classes lose cohesion, split them.

Slide 31

Slide 31 text

Cohesion

Slide 32

Slide 32 text

Coupling ● Coupling is the measure of the degree of interdependence between the modules. ● This isolation makes it easier to understand each element of the system.

Slide 33

Slide 33 text

Coupling

Slide 34

Slide 34 text

Best case scenario Cohesion Coupling

Slide 35

Slide 35 text

Principles 1. DRY 2. KISS 3. YAGNI 4. SOLID

Slide 36

Slide 36 text

● Don’t Repeat Yourself ● Applicable whenever we copy / paste a piece of code D.R.Y.

Slide 37

Slide 37 text

● Keep It Simple and Stupid ● Whenever we want to implement a method to do all things K.I.S.S.

Slide 38

Slide 38 text

● You Ain’t Gonna Need It ● Don’t write code which is not yet necessary Y.A.G.N.I.

Slide 39

Slide 39 text

● Single responsibility (SRP) ● Open-closed (OCP) ● Liskov substitution (LSP) ● Interface segregation (ISP) ● Dependency inversion (DIP) S.O.L.I.D.

Slide 40

Slide 40 text

ERROR HANDLING

Slide 41

Slide 41 text

● Prefer exceptions to returning error codes. ● Error handling is important, but if it obscures logic, it’s wrong. ● In Kotlin we have only unchecked exceptions. ● Define dedicated exception classes. ● Don’t return or pass null. Error handling

Slide 42

Slide 42 text

● IndexOutOfBoundsException ● NoSuchElementException ● IllegalArgumentException ● ... Say YES to standard exceptions

Slide 43

Slide 43 text

fun reportError(): Nothing { throw RuntimeException("Nothing is here") } Nothing is something...

Slide 44

Slide 44 text

fun computeSqrt(number: Double): Double { if(number >= 0) { return Math.sqrt(number) } else { throw RuntimeException("No negative please") } } Nothing is something...

Slide 45

Slide 45 text

fun getMovie(id: Int): Movie { val movie = movieRepository.findMovie(id) return movie ?: throw RuntimeException("Movie not found") } Throw exceptions

Slide 46

Slide 46 text

sealed class MovieSearchResult data class MovieFound(val movie: Movie) : MovieSearchResult() object MovieNotFound : MovieSearchResult() object DatabaseOffline : MovieSearchResult() fun displayMovieResult(movieResult: MovieSearchResult) { when(movieResult) { is MovieFound -> println("yey, we found the movie") is MovieNotFound -> TODO() } } Return result class Missing branches?!

Slide 47

Slide 47 text

val T.exhaustive: T get() = this fun displayMovieResult(movieResult: MovieSearchResult) { when(movieResult) { is MovieFound -> println("yey, we found the movie") is MovieNotFound -> TODO() }.exhaustive } Return result class Compilation error = 'when' expression must be exhaustive, add necessary 'DatabaseOffline' branch or 'else' branch instead (do NOT add else)

Slide 48

Slide 48 text

inputStream.use { outputStream.use { // do something with the streams outputStream.write(inputStream.read()) } } “try-with-resources” in Kotlin - initial solution

Slide 49

Slide 49 text

arrayOf(inputStream, outputStream).use { // do something with the streams outputStream.write(inputStream.read()) } “try-with-resources” in Kotlin - improved solution

Slide 50

Slide 50 text

private inline fun Array.use(block: ()->Unit) { // implementation } use implementation

Slide 51

Slide 51 text

COMMENTS We are best friends forever...

Slide 52

Slide 52 text

Don’t add comments on the bad code, just rewrite it until it’s self-explanatory Comments rule

Slide 53

Slide 53 text

// Check to see if the employee is eligible for full benefits if (employee.rate.equalsIgnoreCase("hours") && employee.objectivesDone > 3) if (employee.isEligibleForFullBenefits())

Slide 54

Slide 54 text

/** Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ Legal comments

Slide 55

Slide 55 text

fun compareTo(o: Any): Int { if (o is Employee) { val employee: Employee = o as Employee val username: String = employee.username val argumentUsername: String = (o as Employee).username return username.compareTo(argumentUsername) } return 1 // we are greater because we are the right type } Explanation of intent

Slide 56

Slide 56 text

fun makeStandardDateFormat(): SimpleDateFormat? { //SimpleDateFormat is not thread safe, //so we need to create each instance independently. val df = SimpleDateFormat("EEE, dd MMM yyyy HH:mm: ss z") df.timeZone = TimeZone.getTimeZone("GMT") return df } Warning of consequences

Slide 57

Slide 57 text

fun sendEmail(employee: Employee) { //TODO send email } TODO comments

Slide 58

Slide 58 text

fun updateUserPoints(user: User, points: Int) { try { user.points += points } catch (e: java.lang.Exception) { // User not found } } Mumbling

Slide 59

Slide 59 text

fun updateUserPoints(user: User, points: Int) { try { user.points += points } // try catch (e: java.lang.Exception) { // User not found println("User not found") } // catch } Redundant & closing brace comments

Slide 60

Slide 60 text

class Log { /** The data. */ var data: String = "" /** The number of data. */ var numberOfData = 0 /** * Secondary constructor. */ constructor(data: String) { } } Noise and scary noise comments

Slide 61

Slide 61 text

Every time you write a comment, a cat is scared somewhere...

Slide 62

Slide 62 text

CODE REVIEW

Slide 63

Slide 63 text

1. Help to improve the quality and consistency of the code 2. Exchange of knowledge and best practices 3. Learn the code base 4. New perspective(s) on your code 5. Learn new tips and tricks about writing code Code Review Advantages

Slide 64

Slide 64 text

Reviewer Author

Slide 65

Slide 65 text

Author

Slide 66

Slide 66 text

● Make sure you understand your task ● Refactor the code if it’s unreadable ● Write tests and follow the team conventions ● Format your code before commit it Writing the code

Slide 67

Slide 67 text

The boy scout rule Leave the campground cleaner than you found it.

Slide 68

Slide 68 text

● Add relevant commit comments ● Send pull requests often ● Have minimum 2 reviewers (one is senior) Before the code review

Slide 69

Slide 69 text

● Be humble ● You are on the same side with your reviewer(s) ● Know when to unlearn the old habits After the code review

Slide 70

Slide 70 text

Reviewer

Slide 71

Slide 71 text

● I think… ● I would… ● I believe… ● I suggest... Use I… comments

Slide 72

Slide 72 text

● Have you consider using… ? ● What do you think about… ? ● Have you tried to… ? Ask questions

Slide 73

Slide 73 text

● This code… ● This function… ● This line of code... It’s about the code, not about the coder

Slide 74

Slide 74 text

Feedback equation* Observation of a behavior Impact of the behavior Question or Request I observed this function has 60 lines. This makes it difficult for me to understand the logic. I suggest extracting a part of the code into other functions and give them relevant names. * Defined by Lara Hogan

Slide 75

Slide 75 text

1. Define with your team a set of conventions 2. Justify technology use 3. Enforce good practices (XP) 4. Question until you understand 5. Criticize ideas, not people 6. Testing, testing, testing 7. Integrate early, integrate often 8. Emphasize collective ownership of code 9. Prioritize and actively evaluate trade-offs 10. Listen to users My summary

Slide 76

Slide 76 text

Coding is not a sprint It’s a marathon