Slide 1

Slide 1 text

Improving Code Quality With Sta7c Analysis Adam McNeilly - @AdamMc331 @AdamMc331 @NYAndroidMeetup 1

Slide 2

Slide 2 text

What Do I Mean By Code Quality? @AdamMc331 @NYAndroidMeetup 2

Slide 3

Slide 3 text

What Do I Mean By Code Quality? • Code should be well forma1ed @AdamMc331 @NYAndroidMeetup 2

Slide 4

Slide 4 text

What Do I Mean By Code Quality? • Code should be well forma1ed • Code should be free of code smells @AdamMc331 @NYAndroidMeetup 2

Slide 5

Slide 5 text

Forma&ng Code With KtLint An an%-bikeshedding Kotlin linter with built-in forma8er.1 "Bikeshedding" is a term used to describe was5ng 5me on trivial details. 1 h$ps:/ /ktlint.github.io/ @AdamMc331 @NYAndroidMeetup 3

Slide 6

Slide 6 text

What Does KtLint Enforce? @AdamMc331 @NYAndroidMeetup 4

Slide 7

Slide 7 text

What Does KtLint Enforce? • Consistent indenta,on @AdamMc331 @NYAndroidMeetup 4

Slide 8

Slide 8 text

What Does KtLint Enforce? • Consistent indenta,on • No wildcard imports @AdamMc331 @NYAndroidMeetup 4

Slide 9

Slide 9 text

What Does KtLint Enforce? • Consistent indenta,on • No wildcard imports • Consistent spacing @AdamMc331 @NYAndroidMeetup 4

Slide 10

Slide 10 text

What Does KtLint Enforce? • Consistent indenta,on • No wildcard imports • Consistent spacing • No empty class bodies @AdamMc331 @NYAndroidMeetup 4

Slide 11

Slide 11 text

What Does KtLint Enforce? • Consistent indenta,on • No wildcard imports • Consistent spacing • No empty class bodies • More: h:ps:/ /github.com/pinterest/ktlint#standard-rules @AdamMc331 @NYAndroidMeetup 4

Slide 12

Slide 12 text

Why Should We Care? @AdamMc331 @NYAndroidMeetup 5

Slide 13

Slide 13 text

Why Should We Care? • Consistency is important @AdamMc331 @NYAndroidMeetup 5

Slide 14

Slide 14 text

Why Should We Care? • Consistency is important • Create a source of truth that everyone agrees on and no one can argue with @AdamMc331 @NYAndroidMeetup 5

Slide 15

Slide 15 text

Why Should We Care? • Consistency is important • Create a source of truth that everyone agrees on and no one can argue with • Allows us to focus on the substance of code reviews instead of styling @AdamMc331 @NYAndroidMeetup 5

Slide 16

Slide 16 text

Adding KtLint To Our Projects @AdamMc331 @NYAndroidMeetup 6

Slide 17

Slide 17 text

Adding KtLint To Our Projects2 // App module build.gradle ktlint { version = "0.35.0" android = true enableExperimentalRules = true reporters = [ReporterType.PLAIN] additionalEditorconfigFile = file("../.editorConfig") } 2 h$ps:/ /github.com/JLLeitschuh/ktlint-gradle @AdamMc331 @NYAndroidMeetup 7

Slide 18

Slide 18 text

Two Helpful Gradle Tasks @AdamMc331 @NYAndroidMeetup 8

Slide 19

Slide 19 text

Two Helpful Gradle Tasks • ./gradlew ktlintCheck @AdamMc331 @NYAndroidMeetup 8

Slide 20

Slide 20 text

Two Helpful Gradle Tasks • ./gradlew ktlintCheck • ./gradlew ktlintFormat @AdamMc331 @NYAndroidMeetup 8

Slide 21

Slide 21 text

KtLint Example // Long line, no spacing data class Test(val name:String,val age:Int,val location:String,val isRegistered:Boolean,val test:String) // Error output: Test.kt:3:1: Exceeded max line length (100) (cannot be auto-corrected) Test.kt:3:17: Parameter should be on a separate line (unless all parameters can fit a single line) Test.kt:3:26: Missing spacing after ":" // Gets formatted into data class Test( val name: String, val age: Int, val location: String, val isRegistered: Boolean, val test: String ) @AdamMc331 @NYAndroidMeetup 9

Slide 22

Slide 22 text

KtLint Example // Long line, no spacing data class Test(val name:String,val age:Int,val location:String,val isRegistered:Boolean,val test:String) // Error output: Test.kt:3:1: Exceeded max line length (100) (cannot be auto-corrected) Test.kt:3:17: Parameter should be on a separate line (unless all parameters can fit a single line) Test.kt:3:26: Missing spacing after ":" // Gets formatted into data class Test( val name: String, val age: Int, val location: String, val isRegistered: Boolean, val test: String ) @AdamMc331 @NYAndroidMeetup 9

Slide 23

Slide 23 text

KtLint Example // Long line, no spacing data class Test(val name:String,val age:Int,val location:String,val isRegistered:Boolean,val test:String) // Error output: Test.kt:3:1: Exceeded max line length (100) (cannot be auto-corrected) Test.kt:3:17: Parameter should be on a separate line (unless all parameters can fit a single line) Test.kt:3:26: Missing spacing after ":" // Gets formatted into data class Test( val name: String, val age: Int, val location: String, val isRegistered: Boolean, val test: String ) @AdamMc331 @NYAndroidMeetup 9

Slide 24

Slide 24 text

Remembering To Format Code Is Hard @AdamMc331 @NYAndroidMeetup 10

Slide 25

Slide 25 text

Auto Format With Git Hook3 // .git/hooks/pre-commit CHANGED_FILES="$(git --no-pager diff --name-status --no-color --cached | awk '$1 != "D" && $2 ~ /\.kts|\.kt/ { print $2}')" ./gradlew --quiet ktlintFormat -PinternalKtlintGitFilter="$CHANGED_FILES" echo "$CHANGED_FILES" | while read -r file; do if [ -f $file ]; then git add $file fi done 3 h$ps:/ /github.com/AdamMc331/PokeDex/blob/master/scripts/git-hooks/pre-commit.sh @AdamMc331 @NYAndroidMeetup 11

Slide 26

Slide 26 text

KtLint Sample @AdamMc331 @NYAndroidMeetup 12

Slide 27

Slide 27 text

Avoid Code Smells With Detekt4 4 h$ps:/ /arturbosch.github.io/detekt/ @AdamMc331 @NYAndroidMeetup 13

Slide 28

Slide 28 text

Avoid Code Smells With Detekt4 • Avoid complex methods 4 h$ps:/ /arturbosch.github.io/detekt/ @AdamMc331 @NYAndroidMeetup 13

Slide 29

Slide 29 text

Avoid Code Smells With Detekt4 • Avoid complex methods • Enforce method counts in classes 4 h$ps:/ /arturbosch.github.io/detekt/ @AdamMc331 @NYAndroidMeetup 13

Slide 30

Slide 30 text

Avoid Code Smells With Detekt4 • Avoid complex methods • Enforce method counts in classes • Limit lines of code in a class/method 4 h$ps:/ /arturbosch.github.io/detekt/ @AdamMc331 @NYAndroidMeetup 13

Slide 31

Slide 31 text

Avoid Code Smells With Detekt4 • Avoid complex methods • Enforce method counts in classes • Limit lines of code in a class/method • Prevent magic numbers 4 h$ps:/ /arturbosch.github.io/detekt/ @AdamMc331 @NYAndroidMeetup 13

Slide 32

Slide 32 text

Why Should We Care? @AdamMc331 @NYAndroidMeetup 14

Slide 33

Slide 33 text

Why Should We Care? • Large collec+ons of code (classes, methods, or whatever else) are unmanageable @AdamMc331 @NYAndroidMeetup 14

Slide 34

Slide 34 text

Why Should We Care? • Large collec+ons of code (classes, methods, or whatever else) are unmanageable • Magic numbers, deeply nested methods, and other code smells can be causes of bugs and make them difficult to resolve @AdamMc331 @NYAndroidMeetup 14

Slide 35

Slide 35 text

Why Should We Care? • Large collec+ons of code (classes, methods, or whatever else) are unmanageable • Magic numbers, deeply nested methods, and other code smells can be causes of bugs and make them difficult to resolve • These sorts of code smells are difficult to detekt5 in a code review 5 I'm sorry... @AdamMc331 @NYAndroidMeetup 14

Slide 36

Slide 36 text

Adding Detekt To Our Projects // App module build.gradle detekt { filters = ".*/resources/.*,.*/build/.*" baseline = file("my-detekt-baseline.xml") config = rootProject.files('detekt-config.yml') } @AdamMc331 @NYAndroidMeetup 15

Slide 37

Slide 37 text

Detekt Is Highly Configurable6 // detekt-config.yml complexity: // Rule Set active: true ComplexMethod: // Rule active: true threshold: 10 ignoreSingleWhenExpression: false ignoreSimpleWhenEntries: false LongParameterList: // Rule active: true threshold: 6 ignoreDefaultParameters: false NestedBlockDepth: // Rule active: true threshold: 4 6 h$ps:/ /github.com/arturbosch/detekt/blob/master/detekt-cli/src/main/resources/default-detekt-config.yml @AdamMc331 @NYAndroidMeetup 16

Slide 38

Slide 38 text

Detekt Is Highly Configurable7 // detekt-config.yml complexity: active: true TooManyFunctions: active: true thresholdInFiles: 11 thresholdInClasses: 11 thresholdInInterfaces: 11 thresholdInObjects: 11 thresholdInEnums: 11 ignoreDeprecated: false ignorePrivate: false ignoreOverridden: false 7 h$ps:/ /github.com/arturbosch/detekt/blob/master/detekt-cli/src/main/resources/default-detekt-config.yml @AdamMc331 @NYAndroidMeetup 17

Slide 39

Slide 39 text

Detekt Tasks @AdamMc331 @NYAndroidMeetup 18

Slide 40

Slide 40 text

Detekt Tasks • ./gradlew detektGenerateConfig @AdamMc331 @NYAndroidMeetup 18

Slide 41

Slide 41 text

Detekt Tasks • ./gradlew detektGenerateConfig • ./gradlew detektBaseline @AdamMc331 @NYAndroidMeetup 18

Slide 42

Slide 42 text

Detekt Tasks • ./gradlew detektGenerateConfig • ./gradlew detektBaseline • ./gradlew detekt @AdamMc331 @NYAndroidMeetup 18

Slide 43

Slide 43 text

HTML Report Output Example @AdamMc331 @NYAndroidMeetup 19

Slide 44

Slide 44 text

Novoda Gradle Sta-c Analysis Plugin8 // ./gradlew evaluateViolations // Will run all the tools below staticAnalysis { penalty { maxErrors = 0 maxWarnings = 0 } ktlint { } detekt { } lintOptions { } ... { } } 8 h$ps:/ /github.com/novoda/gradle-sta:c-analysis-plugin @AdamMc331 @NYAndroidMeetup 20

Slide 45

Slide 45 text

When Should I Run This Process? @AdamMc331 @NYAndroidMeetup 21

Slide 46

Slide 46 text

When Should I Run This Process? • As a step in CI flow @AdamMc331 @NYAndroidMeetup 21

Slide 47

Slide 47 text

When Should I Run This Process? • As a step in CI flow • Pre push hook @AdamMc331 @NYAndroidMeetup 21

Slide 48

Slide 48 text

When Should I Run This Process? • As a step in CI flow • Pre push hook • Make your build task depend on it @AdamMc331 @NYAndroidMeetup 21

Slide 49

Slide 49 text

Some Best Prac-ces I just added these tools, my build failed, what do I do? @AdamMc331 @NYAndroidMeetup 22

Slide 50

Slide 50 text

Some Best Prac-ces I just added these tools, my build failed, what do I do? • Fix it! @AdamMc331 @NYAndroidMeetup 22

Slide 51

Slide 51 text

Some Best Prac-ces I just added these tools, my build failed, what do I do? • Fix it! • Modify the threshold for the relevant error @AdamMc331 @NYAndroidMeetup 22

Slide 52

Slide 52 text

Some Best Prac-ces I just added these tools, my build failed, what do I do? • Fix it! • Modify the threshold for the relevant error • Suppress this individual case @AdamMc331 @NYAndroidMeetup 22

Slide 53

Slide 53 text

Some Best Prac-ces I just added these tools, my build failed, what do I do? • Fix it! • Modify the threshold for the relevant error • Suppress this individual case • Turn off this rule or rule set @AdamMc331 @NYAndroidMeetup 22

Slide 54

Slide 54 text

Thank You! Sample app: h*ps:/ /github.com/adammc331/pokedex Slides: h*p:/ /bit.ly/NYAndroid-StaAcAnalysis @AdamMc331 @NYAndroidMeetup 23