Slide 1

Slide 1 text

Code readability Munetoshi Ishikawa

Slide 2

Slide 2 text

Related book ಡΈ΍͍͢ίʔυͷΨΠυϥΠϯ - ࣋ଓՄೳͳιϑτ΢ΣΞ։ൃͷͨΊʹ - Published on Oct. 22, 2023. ٕज़ධ࿦ࣾ - Written in Japanese https://gihyo.jp/book/ 2022/978-4-297-13036-7

Slide 3

Slide 3 text

Code readability session 1 Introduction and Principles

Slide 4

Slide 4 text

What readable code is - Obvious: isVisible rather than flag - Simple: isA && isB rather than !(!isA || !isB) && isB - Isolated: functions, classes, modules, etc. - Structured: format, dependency, state transition, etc. Multiple characteristics must be considered Readable code depends on time and context Introduction and Principles > Introduction

Slide 5

Slide 5 text

Why we need readable code To keep high productivity even for a large scale product Introduction and Principles > Introduction

Slide 6

Slide 6 text

Product scale and productivity Large scale development → Hard to keep high productivity value, outcome development scale (code, headcount, time) Readability: One of the methods to realize high productivity Introduction and Principles > Introduction

Slide 7

Slide 7 text

Product scale and readability Reading code > Writing code at a large scale product - Understanding existing code to implement a new feature - Requesting code review from two or more engineers - Complicated bug fix with only one line Easy to read is more important than easy to write Introduction and Principles > Introduction

Slide 8

Slide 8 text

Productivity optimization Focus on team productivity during the entire product lifecycle Your 5 minutes could save 1 hour for others Add a comment, write test, refactor We may need to update personnel rating criteria Don't focus only on short-term speed to implement Introduction and Principles > Introduction

Slide 9

Slide 9 text

Prisoner's dilemma Team productivity may decrease if we focus on the personal productivity Bob Alice Hacky Readable Readable Hacky 8 8 10 1 1 10 2 2 Introduction and Principles > Introduction

Slide 10

Slide 10 text

How to work on improving readability - Choose techniques and knowledge: Remember the objective - Balance value and complexity - Take advantages of automatic validation: Compiler, test, etc. - Discuss frequently: Reduce rework of mistakes - Keep learning Introduction and Principles > Introduction

Slide 11

Slide 11 text

Learn how to write readable code Feature implementation is relatively easy Special training is not required No learning, No readable code - lectures, training, books, online articles - peer code review, pair programming Introduction and Principles > Introduction

Slide 12

Slide 12 text

Contents of this lecture - Introduction and Principles - Natural language: Naming, Comments - Inner type structure: State, Function - Inter type structure: Dependency I, Dependency II - Follow-up: Review Introduction and Principles > Introduction

Slide 13

Slide 13 text

Contents of this lecture - Introduction and Principles - Natural language: Naming, Comments - Inner type structure: State, Function - Inter type structure: Dependency I, Dependency II - Follow-up: Review Introduction and Principles > Introduction

Slide 14

Slide 14 text

Topics - Introduction - The boy scout rule - YAGNI - KISS - Single responsibility principle - Premature optimization is the root of all evil Introduction and Principles > Principles

Slide 15

Slide 15 text

Topics - Introduction - The boy scout rule - YAGNI - KISS - Single responsibility principle - Premature optimization is the root of all evil Introduction and Principles > Principles > The boy scout rule

Slide 16

Slide 16 text

The boy scout rule Try to leave this world a little better than you found it... — Robert Baden-Powell Introduced to software development by Robert C. Martin1 Improve code whenever you touch it 1 97 Things Every Programmer Should Know: Collective Wisdom from the Experts, Kevlin Henney, 2010 Introduction and Principles > Principles > The boy scout rule

Slide 17

Slide 17 text

Dos for the boy scout rule - Add: comments, tests - Remove: unnecessary dependencies, members, conditions - Rename: classes, functions, variables - Break: huge classes, huge functions, nests, call sequences - Structure: format, dependencies, abstraction layers, hierarchies Introduction and Principles > Principles > The boy scout rule

Slide 18

Slide 18 text

Don'ts for the boy scout rule Don't add an element in a huge structure Examples - Don't add a new member/sentence into a huge class/function - Don't add a new layer into a deep call/type hierarchy Introduction and Principles > Principles > The boy scout rule

Slide 19

Slide 19 text

Example of dos and don'ts 1/3 Question: Is it fine to add a new enum type Z? val viewType: ViewType = ... // An enum type when (viewType) { A -> { view1.isVisible = true view2.text = "Case A" } B -> { view1.isVisible = false view2.text = "Case B" } ... Introduction and Principles > Principles > The boy scout rule

Slide 20

Slide 20 text

Example of dos and don'ts 2/3 Answer: Should not add as is There are already too many conditional branches Solution: Extract values as enum properties Introduction and Principles > Principles > The boy scout rule

Slide 21

Slide 21 text

Example of dos and don'ts 3/3 1: Extract values as properties enum class ViewType(val isView1Visible: Boolean, val view2Text: String) 2: Remove conditional branches with the properties view1.isVisible = viewType.isView1Visible view2.text = viewType.view2Text 3: Add a new type Z Introduction and Principles > Principles > The boy scout rule

Slide 22

Slide 22 text

The boy scout rule: Note Don't make too large of a pull-request or commit - Refactor before implementing Consider the affected area - Decide the scope of refactoring in advance - Don't refactor at the release branch Introduction and Principles > Principles > The boy scout rule

Slide 23

Slide 23 text

Topics - Introduction - The boy scout rule - YAGNI - KISS - Single responsibility principle - Premature optimization is the root of all evil Introduction and Principles > Principles > YAGNI

Slide 24

Slide 24 text

YAGNI You Aren't Gonna Need It Implement only when required - 90% of features for the future are not used 2 - Keep structure simple = ready for unexpected change 2 2 http://www.extremeprogramming.org/rules/early.html Introduction and Principles > Principles > YAGNI

Slide 25

Slide 25 text

YAGNI: Example 1/2 Unused code - Classes/functions/variables not referred - Code commented out Overextended code - Abstract type with only one implementation - Function parameter only for a constant value Introduction and Principles > Principles > YAGNI

Slide 26

Slide 26 text

YAGNI: Example 2/2 Add a unit type into UI coordinate model class Coordinate(val x: Int, val y: Int, val unitType: UnitType) enum class UnitType { PIXEL, POINT } The definition of Coordinate + Coordinate will be complicated An unused feature can easily lead to inappropriate design Introduction and Principles > Principles > YAGNI

Slide 27

Slide 27 text

YAGNI: Note Focuses on feature implementation - Discussion is necessary: design, whether feature is needed Assumes that the code is modifiable - Needs attention for public API or external library - Requires system to update Introduction and Principles > Principles > YAGNI

Slide 28

Slide 28 text

Topics - Introduction - The boy scout rule - YAGNI - KISS - Single responsibility principle - Premature optimization is the root of all evil Introduction and Principles > Principles > KISS

Slide 29

Slide 29 text

KISS Keep It Simple Stupid — Clarence Leonard "Kelly" Johnson Choose a simpler solution - Use the standard implementation as far as possible - Limit and specify the usage of a library/framework/design Beautiful code is not always readable Introduction and Principles > Principles > KISS

Slide 30

Slide 30 text

KISS: Bad example return userActionLog .groupBy { it.user } .map { it.key to it.value.size } .sortedBy { it.second } .map { it.first } .takeLast(10) Need to read all to understand: receiver, it Introduction and Principles > Principles > KISS

Slide 31

Slide 31 text

KISS: Bad example return userActionLog .groupBy ... .map ... .sortedBy ... .map ... .takeLast(10) Need to read all to understand: receiver, it Introduction and Principles > Principles > KISS

Slide 32

Slide 32 text

KISS: Good example val logCountByUser: Map = userActionLog .groupBy { log -> log.user } .map { (user, logs) -> user to logs.size } val userListSortedByLogCount: List = logCountByUser .sortedBy { (_, messageCount) -> messageCount } .map { (userId, _) -> userId } return userListSortedByLogCount.takeLast(10) Does not look beautiful, but easier to understand Introduction and Principles > Principles > KISS

Slide 33

Slide 33 text

KISS: Good example val logCountByUser: Map = userActionLog ... ... val userListSortedByLogCount: List = logCountByUser ... ... return userListSortedByLogCount.takeLast(10) Does not look beautiful, but easier to understand Introduction and Principles > Principles > KISS

Slide 34

Slide 34 text

Topics - Introduction - The boy scout rule - YAGNI - KISS - Single responsibility principle - Premature optimization is the root of all evil Introduction and Principles > Principles > Single responsibility principle

Slide 35

Slide 35 text

Single method == small responsibilityʁ No. Number of methods != Amount of responsibility class Alviss { // May show a text, may break the device, may launch a rocket, // may ... fun doEverything(state: UniverseState) } Introduction and Principles > Principles > Single responsibility principle

Slide 36

Slide 36 text

Single responsibility principle A class should have only one reason to change. — Robert C. Martin Should not mix up two unrelated features Introduction and Principles > Principles > Single responsibility principle

Slide 37

Slide 37 text

Single responsibility principle: Bad example 1/2 Book rental state of a library Book rental status Definition and list of books Definition and list of users Entries of rental status (user, due date) Introduction and Principles > Principles > Single responsibility principle

Slide 38

Slide 38 text

Single responsibility principle: Bad example 2/2 class LibraryBookRentalData( val bookIds: MutableList, val bookNames: MutableList, val bookIdToRenterNameMap: MutableMap, val bookIdToDueDateMap: MutableMap, ... ) { fun findRenterName(bookName: String): String? fun findDueDate(bookName: String): Date? ... Introduction and Principles > Principles > Single responsibility principle

Slide 39

Slide 39 text

Single responsibility principle: How to improve 1/2 Split a model class for each entity Rental state model Definition of book Definition of user Entry of rental state (user, due date) Introduction and Principles > Principles > Single responsibility principle

Slide 40

Slide 40 text

Single responsibility principle: How to improve 2/2 data class BookData(val id: Int, val name: String, ...) data class UserData(val name: String, ...) class CirculationRecord( val onLoanBookEntries: MutableMap ) { data class Entry(val renter: UserData, val dueDate: Date) Introduction and Principles > Principles > Single responsibility principle

Slide 41

Slide 41 text

Keep responsibility small Split classes - Model for each entity - Logic for each layer and component - Utility for each target type Introduction and Principles > Principles > Single responsibility principle

Slide 42

Slide 42 text

How to confirm responsibility List what the class does and try to summarize it Split the class for each case as follows: - It's hard to make a summary - The summary is too long compared with the name Introduction and Principles > Principles > Single responsibility principle

Slide 43

Slide 43 text

Topics - Introduction - The boy scout rule - YAGNI - KISS - Single responsibility principle - Premature optimization is the root of all evil Introduction and Principles > Principles > Premature optimization

Slide 44

Slide 44 text

Premature optimization 1/2 We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. — Structured Programming with go to Statements, Donald Knuth Introduction and Principles > Principles > Premature optimization

Slide 45

Slide 45 text

Premature optimization 2/2 Good optimization: makes the code simpler Bad optimization: makes the code complex Introduction and Principles > Principles > Premature optimization

Slide 46

Slide 46 text

Example of good optimization Before optimization: val data = arrayList.find { data -> data.key == expectedKey } After optimization: val data = hashMap[expectedKey] Simplify the code while reducing the calculation cost Introduction and Principles > Principles > Premature optimization

Slide 47

Slide 47 text

Optimization making code complex 1/2 Yet we should not pass up our opportunities in that critical 3% — Donald Knuth Introduction and Principles > Principles > Premature optimization

Slide 48

Slide 48 text

Optimization making code complex 2/2 - Mutable instance reusing - Cache - Instance pool - Lazy initialization Requires platform support or profiling Introduction and Principles > Principles > Premature optimization

Slide 49

Slide 49 text

Drawbacks of optimization May obstruct simplification - Optimizer may be smarter May require overhead cost - Cache: cache miss ratio access time - Lazy initialization: (synchronized) instance check Introduction and Principles > Principles > Premature optimization

Slide 50

Slide 50 text

Required actions before optimization Check if the optimization is reasonable Balance value and complexity Profile/estimate Target (time, memory), amount, rate Introduction and Principles > Principles > Premature optimization

Slide 51

Slide 51 text

Summary Readability is for sustainable development The boy scout rule: Make code readable before changing it YAGNI: Implement only when required KISS: Choose a simple solution, beautiful != readable Single responsibility principle: Clarify the scope Premature optimization: Profile/estimate before optimization Introduction and Principles > Summary