Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Reviewing Kotlin (Conference for Kotliners 2019)

Reviewing Kotlin (Conference for Kotliners 2019)

I have been teaching our rapidly growing team of Android developers Kotlin for about a year, and for the last few months, I’ve been reviewing tens of thousands of lines of code written by almost a dozen people at times. Here’s what we’ve found out together about learning, teaching, and reviewing Kotlin. I’ll tell you what worked for us and what didn’t, so that you may be more prepared for this path than we were. I’ll also point out some of the issues that most often arose in the code while our developers were getting familiar with the language.

Recording and resources: https://zsmb.co/talks/reviewing-kotlin/

Marton Braun

June 07, 2019
Tweet

More Decks by Marton Braun

Other Decks in Programming

Transcript

  1. Reviewing Kotlin
    Márton Braun
    zsmb.co zsmb13
    [email protected]

    View full-size slide

  2. This is not…

    View full-size slide

  3. How great code reviews are, and what’s
    the best way to do them
    This is not…

    View full-size slide

  4. and how to get your
    management onboard with the idea
    Why you should use Kotlin for Android
    development…
    How great code reviews are, and what’s
    the best way to do them
    This is not…

    View full-size slide

  5. and how to get your
    management onboard with the idea
    Why you should use Kotlin for Android
    development…
    All the awesome things that are in the
    Kotlin standard library
    How great code reviews are, and what’s
    the best way to do them
    This is not…

    View full-size slide

  6. Life is Great and Everything Will Be Ok,
    Kotlin is Here
    Christina Lee, Jake Wharton
    Google I/O '17
    Dissecting the stdlib
    Huyen Tue Dao
    KotlinConf 2018
    Code Review Best Practices
    Trisha Gee
    SCLConf 2018
    This is not…

    View full-size slide

  7. 99000 lines of Kotlin code
    5700 commits
    700 merge requests

    View full-size slide

  8. Breaking the habit

    View full-size slide

  9. Breaking the habit
    Enums

    View full-size slide

  10. Breaking the habit
    Enums Lambdas

    View full-size slide

  11. Breaking the habit
    Enums Lambdas Typechecks

    View full-size slide

  12. Breaking the habit
    Enums Lambdas Typechecks
    is
    as
    as?

    View full-size slide

  13. entries: ArrayList) {
    fun updateColors(chart: ,
    }
    PieChart

    View full-size slide

  14. entries: ArrayList) {
    fun updateColors(chart: ,
    val colors = ArrayList()
    }
    PieChart

    View full-size slide

  15. entries: ArrayList) {
    fun updateColors(chart: ,
    val colors = ArrayList()
    entries.forEach { entry ->
    colors.add(entry.data as Int)
    }
    }
    PieChart

    View full-size slide

  16. entries: ArrayList) {
    fun updateColors(chart: ,
    val colors = ArrayList()
    entries.forEach { entry ->
    colors.add(entry.data as Int)
    }
    chart.colors = colors
    }
    PieChart

    View full-size slide

  17. entries: ArrayList) {
    fun updateColors(chart: ,
    val colors = ArrayList()
    entries.forEach { entry ->
    colors.add(entry.data as Int)
    }
    chart.colors = colors
    }
    PieChart

    View full-size slide

  18. fun PieChart.updateColors(entries:
    val colors = ArrayList()
    entries.forEach { entry ->
    colors.add(entry.data as Int)
    }
    this.colors = colors
    }
    List) {
    Array

    View full-size slide

  19. fun PieChart.updateColors(entries:
    val colors = ArrayList()
    entries.forEach { entry ->
    colors.add(entry.data as Int)
    }
    this.colors = colors
    }
    List) {

    View full-size slide

  20. fun PieChart.updateColors(entries:
    val colors = ArrayList()
    entries.forEach { entry ->
    colors.add(entry.data as Int)
    }
    this.colors = colors
    }
    List) {

    View full-size slide

  21. fun PieChart.updateColors(entries: List) {
    val colors = mutableListOf()
    entries.forEach { entry ->
    colors.add(entry.data as Int)
    }
    this.colors = colors
    }

    View full-size slide

  22. fun PieChart.updateColors(entries: List) {
    val colors = mutableListOf()
    entries.forEach { entry ->
    colors.add(entry.data as Int)
    }
    this.colors = colors
    }

    View full-size slide

  23. fun PieChart.updateColors(entries: List) {
    val colors = mutableListOf()
    entries.map { entry ->
    colors.add( )
    }
    this.colors = colors
    }
    entry.data as Int

    View full-size slide

  24. fun PieChart.updateColors(entries: List) {
    val colors = mutableListOf()
    entries.map { entry ->
    }
    this.colors = colors
    }
    entry.data as Int

    View full-size slide

  25. fun PieChart.updateColors(entries: List) {
    this.colors = colors
    }
    entry.data as Int
    }
    entries.map { entry ->
    val colors =

    View full-size slide

  26. fun PieChart.updateColors(entries: List) {
    this.colors = colors
    }
    entry.data as Int
    }
    entries.map { entry ->
    val colors =

    View full-size slide

  27. fun PieChart.updateColors(entries: List) {
    this.colors
    entry.data as Int
    }
    }
    = entries.map { entry ->

    View full-size slide

  28. fun getExaminationResult(resultId: UUID): ExaminationResult {
    }

    View full-size slide

  29. fun getExaminationResult(resultId: UUID): ExaminationResult {
    return getItemGroups()
    }

    View full-size slide

  30. fun getExaminationResult(resultId: UUID): ExaminationResult {
    return getItemGroups()
    }
    .map { it.toExaminationResult() }

    View full-size slide

  31. fun getExaminationResult(resultId: UUID): ExaminationResult {
    return getItemGroups()
    }
    .map { it.toExaminationResult() }
    .first { it.id == resultId }

    View full-size slide

  32. fun getExaminationResult(resultId: UUID): ExaminationResult {
    return getItemGroups()
    }
    .map { it.toExaminationResult() }
    .first { it.id == resultId }

    View full-size slide

  33. fun getExaminationResult(resultId: UUID): ExaminationResult {
    return getItemGroups()
    }
    .map { it.toExaminationResult() }
    .first { it.id == resultId }

    View full-size slide

  34. fun getExaminationResult(resultId: UUID): ExaminationResult {
    return getItemGroups()
    }
    .first { it.id == resultId }
    .map { it }
    .toExaminationResult()

    View full-size slide

  35. fun getExaminationResult(resultId: UUID): ExaminationResult {
    return getItemGroups()
    }
    .first { it.id == resultId }
    .toExaminationResult()

    View full-size slide

  36. val events: List = getAllEvents()
    val upcoming = events.filter {
    it.date >
    }
    OffsetDateTime.now()

    View full-size slide

  37. val events: List = getAllEvents()
    val upcoming = events.filter {
    it.date >
    }
    OffsetDateTime.now()
    val now =
    now

    View full-size slide

  38. fun startBluetoothLeScan() {
    }

    View full-size slide

  39. fun startBluetoothLeScan() {
    val scanner = BluetoothAdapter.getDefaultAdapter()
    .bluetoothLeScanner
    }

    View full-size slide

  40. fun startBluetoothLeScan() {
    val scanner = BluetoothAdapter.getDefaultAdapter()
    .bluetoothLeScanner
    /* ... */
    }

    View full-size slide

  41. fun startBluetoothLeScan() {
    val scanner = BluetoothAdapter.getDefaultAdapter()
    .bluetoothLeScanner
    /* ... */
    scanner
    }
    .startScan(/* ... */)

    View full-size slide

  42. fun startBluetoothLeScan() {
    val scanner = BluetoothAdapter.getDefaultAdapter()
    .bluetoothLeScanner
    /* ... */
    scanner?
    }
    .startScan(/* ... */)

    View full-size slide

  43. if (scanner != null) {
    } else {
    /* ¯\_(ツ)_/¯ */
    }
    }
    fun startBluetoothLeScan() {
    val scanner = BluetoothAdapter.getDefaultAdapter()
    .bluetoothLeScanner
    /* ... */
    scanner.startScan(/* ... */)

    View full-size slide

  44. data class DailyFluidConsumption(
    )

    View full-size slide

  45. data class DailyFluidConsumption(
    val quantity: Double,
    )

    View full-size slide

  46. data class DailyFluidConsumption(
    val quantity: Double,
    val recommended: Double = 4.0,
    )

    View full-size slide

  47. data class DailyFluidConsumption(
    val quantity: Double,
    val recommended: Double = 4.0,
    val percentage: Int =
    ((quantity / recommended) * 100)
    .roundToInt()
    .coerceIn(0..100)
    )

    View full-size slide

  48. data class DailyFluidConsumption(
    val quantity: Double,
    val recommended: Double = 4.0
    )
    val percentage: Int =
    ((quantity / recommended) * 100)
    .roundToInt()
    .coerceIn(0..100)

    View full-size slide

  49. data class DailyFluidConsumption(
    val quantity: Double,
    val recommended: Double = 4.0
    )
    val DailyFluidConsumption.percentage: Int
    ((quantity / recommended) * 100)
    .roundToInt()
    .coerceIn(0..100)

    View full-size slide

  50. data class DailyFluidConsumption(
    val quantity: Double,
    val recommended: Double = 4.0
    )
    val DailyFluidConsumption.percentage: Int
    get() = ((quantity / recommended) * 100)
    .roundToInt()
    .coerceIn(0..100)

    View full-size slide

  51. zsmb.co/data-classes-arent-that-magical

    View full-size slide

  52. class Ingredient(
    val id: UUID = UUID.randomUUID(),
    val name: String = "",
    val quantity: Double? = null,
    val unit: String? = null,
    val imageId: UUID? = null
    )

    View full-size slide

  53. class Ingredient(
    val id: UUID = UUID.randomUUID(),
    val name: String = "",
    val quantity: Double? = null,
    val unit: String? = null,
    val imageId: UUID? = null
    )

    View full-size slide

  54. class Ingredient(
    val id: UUID = UUID.randomUUID(),
    val name: String = "",
    val quantity: Double? = null,
    val unit: String? = null,
    val imageId: UUID? = null
    )

    View full-size slide

  55. class Ingredient(
    val id: UUID = UUID.randomUUID(),
    val name: String = "",
    val quantity: Double? = null,
    val unit: String? = null,
    val imageId: UUID? = null
    )
    Ingredient(id, "Pixie dust", 6.28, "teaspoon")

    View full-size slide

  56. class Ingredient(
    val id: UUID = UUID.randomUUID(),
    val name: String = "",
    val quantity: Double? = null,
    val unit: String? = null,
    val imageId: UUID? = null
    )
    Ingredient(id, "Pixie dust", 6.28, "teaspoon")

    View full-size slide

  57. class Ingredient(
    val id: UUID = UUID.randomUUID(),
    val name: String = "",
    val quantity: Double? = null,
    val unit: String? = null,
    val imageId: UUID? = null
    )
    Ingredient(id, "Pixie dust", 6.28, "teaspoon")

    View full-size slide

  58. class Ingredient(
    val id: UUID,
    val name: String,
    val quantity: Double?,
    val unit: String?,
    val imageId: UUID?
    )
    Ingredient(id, "Pixie dust", 6.28, "teaspoon")

    View full-size slide

  59. class Ingredient(
    val id: UUID,
    val name: String,
    val quantity: Double?,
    val unit: String?,
    val imageId: UUID?
    )
    Ingredient(id, "Pixie dust", 6.28, "teaspoon", null)

    View full-size slide

  60. id =
    name =
    quantity =
    unit =
    imageId =
    id,
    6.28,
    )
    "teaspoon",
    null
    Ingredient(
    "Pixie dust",
    class Ingredient(
    val id: UUID,
    val name: String,
    val quantity: Double?,
    val unit: String?,
    val imageId: UUID?
    )

    View full-size slide

  61. suspend fun getValidMeasurements(
    measurements: List
    ): List {
    }

    View full-size slide

  62. suspend fun getValidMeasurements(
    measurements: List
    ): List {
    return measurements.filter {
    }
    }

    View full-size slide

  63. suspend fun getValidMeasurements(
    measurements: List
    ): List {
    return measurements.filter {
    val validator = getValidatorForType(it.type)
    }
    }

    View full-size slide

  64. suspend fun getValidMeasurements(
    measurements: List
    ): List {
    return measurements.filter {
    val validator = getValidatorForType(it.type)
    it.value in (validator.minValue..validator.maxValue)
    }
    }

    View full-size slide

  65. suspend fun getValidMeasurements(
    measurements: List
    ): List {
    return measurements.filter { measurement ->
    val validator = getValidatorForType(measurement.type)
    measurement.value in (validator.minValue..validator.maxValue)
    }
    }

    View full-size slide

  66. return measurements.filter { measurement ->
    val validator = getValidatorForType(measurement.type)
    measurement.value in (validator.minValue..validator.maxValue)
    }
    }
    suspend fun getValidMeasurements(
    measurements: List
    ): List {

    View full-size slide

  67. return measurements.filter { measurement ->
    val validator = getValidatorForType(measurement.type)
    measurement.value in (validator.minValue..validator.maxValue)
    }
    }
    suspend fun getValidMeasurements(
    measurements: List
    ): List {

    View full-size slide

  68. return measurements.filter { measurement ->
    val validator = getValidatorForType(measurement.type)
    measurement.value in (validator.minValue..validator.maxValue)
    }
    }
    suspend fun getValidMeasurements(
    measurements: List
    ): List {

    View full-size slide

  69. val validatorsByType = getAllValidators()
    suspend fun getValidMeasurements(
    measurements: List
    ): List {
    }
    return measurements.filter { measurement ->
    val validator = getValidatorForType(measurement.type)
    measurement.value in (validator.minValue..validator.maxValue)
    }

    View full-size slide

  70. val validatorsByType = getAllValidators().associateBy { it.type }
    suspend fun getValidMeasurements(
    measurements: List
    ): List {
    }
    return measurements.filter { measurement ->
    val validator = getValidatorForType(measurement.type)
    measurement.value in (validator.minValue..validator.maxValue)
    }

    View full-size slide

  71. val validatorsByType = getAllValidators()
    suspend fun getValidMeasurements(
    measurements: List
    ): List {
    }
    return measurements.filter { measurement ->
    val validator = validatorsByType.getValue(measurement.type)
    measurement.value in (validator.minValue..validator.maxValue)
    }
    .associateBy { it.type }

    View full-size slide

  72. fun add (x:Int,y: Int) :Int{
    return x +y
    }
    fun main() {
    println( add(2,3) )
    }

    View full-size slide

  73. fun add(x: Int, y: Int): Int {
    return x + y
    }
    fun main() {
    println(add(2, 3))
    }

    View full-size slide

  74. What did you miss about Java?

    View full-size slide

  75. Related talks
    • Code Review Best Practices
     Trisha Gee, SCLConf 2018
     https://www.youtube.com/watch?v=jXi8h44cbQA
    • Life is Great and Everything Will Be Ok, Kotlin is Here
     Christina Lee & Jake Wharton, Google I/O ‘17
     https://www.youtube.com/watch?v=fPzxfeDJDzY
    • Dissecting the stdlib
     Huyen Tue Dao, KotlinConf 2018
     https://www.youtube.com/watch?v=Fzt_9I733Yg

    View full-size slide

  76. Learning resources
    • Kotlin in Action, Dmitry Jemerov and Svetlana Isakova
     https://www.manning.com/books/kotlin-in-action
    • Coursera course, Andrey Breslav and Svetlana Isakova
     https://www.coursera.org/learn/kotlin-for-java-developers
    • O’Reilly courses, Hadi Hariri
     https://hadihariri.com/2016/11/01/oreilly-kotlin-course/

    View full-size slide

  77. Learning resources
    • Kotlin Bootcamp for Programmers
     https://eu.udacity.com/course/kotlin-bootcamp-for-
    programmers--ud9011
    • Developing Android Apps with Kotlin
     https://eu.udacity.com/course/developing-android-apps-with-
    kotlin--ud9012

    View full-size slide

  78. Further reading
    • Data classes aren’t (that) magical
     https://zsmb.co/data-classes-arent-that-magical/

    View full-size slide

  79. zsmb13
    zsmb.co/talks

    View full-size slide

  80. Questions?
    Márton Braun
    zsmb.co/talks
    zsmb13
    [email protected]

    View full-size slide