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

Poniendo Kotlin en producción a palos

Tuenti
November 25, 2017

Poniendo Kotlin en producción a palos

Tuenti

November 25, 2017
Tweet

More Decks by Tuenti

Other Decks in Programming

Transcript

  1. Poniendo
    by @andres_viedma
    en producción
    A palos
    MAD · NOV 24-25 · 2017

    View Slide

  2. Andrés Viedma
    Andrés Viedma
    @andres_viedma
    @andres_viedma

    View Slide

  3. 01 KOTLIN

    View Slide

  4. View Slide

  5. Null safety
    fun printStringLength(maybeString: String?) {
    // maybeString.length would not compile
    if (maybeString != null) {
    // maybeString cannot be null now, it's a String
    println(maybeString.length)
    } else {
    println("")
    }
    }
    ”My billion dollars mistake”
    Tony Hoare

    View Slide

  6. fun printStringLength1(maybeString: String?) {
    maybeString?.let { s -> println(s) }
    }
    fun printStringLengthOrEmpty(maybeString: String?) {
    println(maybeString?.length ?: "")
    }
    Null safety

    View Slide

  7. A small immutable Kotlin class
    data class TokenInfo(
    val tokenType: String = “auth”,
    val identity: String
    val expiration: Int? = null
    )

    View Slide

  8. Constructor with Properties
    class TokenInfo(
    val tokenType: String,
    val identity: String
    (…)
    )
    public final class TokenInfo {
    private final String tokenType;
    private final String identity;
    (…)
    public TokenInfo(
    String tokenType,
    String identity,
    (…)) {
    this.tokenType = tokenType;
    this.identity = identity;
    (…)
    }
    public final String getTokenType() {
    return tokenType;
    }
    public final String getIdentity() {
    return identity;
    }
    }

    View Slide

  9. Getters and Setters
    class TokenInfo(
    val tokenType: String,
    val identity: String
    (…)
    )
    public final class TokenInfo {
    private final String tokenType;
    private final String identity;
    (…)
    public TokenInfo(
    String tokenType,
    String identity,
    (…)) {
    this.tokenType = tokenType;
    this.identity = identity;
    (…)
    }
    public final String getTokenType() {
    return tokenType;
    }
    public final String getIdentity() {
    return identity;
    }
    }

    View Slide

  10. Named arguments and optional values
    class TokenInfo(
    val tokenType: String = "auth",
    val identity: String,
    val expiration: Int? = null
    )
    val token = TokenInfo(identity = "xxx")
    Immutable classes with
    optional fields?
    Constructors with lots of
    parameters
    Builder object

    View Slide

  11. Data classes
    data class TokenInfo(
    val tokenType: String,
    val identity: String
    (…)
    )
    public final class TokenInfo {
    (…)
    public String toString() { (…) }
    public int hashCode() { (…) }
    public boolean equals(Object var1) { (…) }
    public final TokenInfoId copy(String tokenType,
    String identity) {
    (…)
    }
    public final String component1() { (…) }
    public final String component2() { (…) }
    }
    Immutability
    made easier

    View Slide

  12. Collections improvements
    List persons = Arrays.asList(
    new Person("Sansa", "Stark"),
    new Person("Jon", "Snow"));
    List personNames = persons.stream()
    .map(p -> p.getName() + " " + p.getSurname())
    .collect(Collectors.toList());
    val persons = listOf(
    Person("Sansa", "Stark"),
    Person("Jon", "Snow"))
    val personNames = persons
    .map { p -> "${p.name} ${p.surname}" }
    Extension functions

    View Slide

  13. Collections improvements
    List result = list.stream()
    .flatMap(o -> o.isPresent() ?
    Stream.of(o.get()) : Stream.empty())
    .collect(Collectors.toList());
    val result = list.filter { it != null }
    List result = list.stream()
    .flatMap(o → o.map(Stream::of)
    .orElseGet(Stream::empty))
    .collect(Collectors.toList());
    List result = list.stream()
    .flatMap(Optional::stream)
    .collect(Collectors.toList());

    View Slide

  14. String Interpolation
    List persons = Arrays.asList(
    new Person("Sansa", "Stark"),
    new Person("Jon", "Snow"));
    List personNames = persons.stream()
    .map(p -> p.getName() + " " + p.getSurname())
    .collect(Collectors.toList());
    val persons = listOf(
    Person("Sansa", "Stark"),
    Person("Jon", "Snow"))
    val personNames = persons
    .map { p -> "${p.name} ${p.surname}" }

    View Slide

  15. Type Inference
    val persons = listOf(
    Person("Sansa", "Stark"),
    Person("Jon", "Snow"))
    val personNames = persons
    .map { p -> "${p.name} ${p.surname}" }
    List persons = Arrays.asList(
    new Person("Sansa", "Stark"),
    new Person("Jon", "Snow"));
    List personNames = persons.stream()
    .map(p -> p.getName() + " " + p.getSurname())
    .collect(Collectors.toList());

    View Slide

  16. Gotcha! Now the idea is trapped inside your head

    View Slide

  17. Gotcha! Now the idea is trapped inside your head

    View Slide

  18. Gotcha! Now the idea is trapped inside your head

    View Slide

  19. 02 LET'S DO IT!

    View Slide

  20. Tell the team!
    The conformist

    View Slide

  21. Tell the team!
    The conformist The idealist

    View Slide

  22. Tell the team!
    The conformist The idealist The responsible

    View Slide

  23. Tell the team!
    The conformist The idealist The responsible
    AGGG ROOO RAGG
    AGGG ROOO RAGG
    ROOOOOOOGHH!
    ROOOOOOOGHH!

    View Slide

  24. What about the rest of the company?

    View Slide

  25. Option 1...

    View Slide

  26. Option 2: Talk to them

    View Slide


  27. Can affect development time

    Misunderstandings can increase bug rate

    Service responsibility is not fixed
    Not just YOUR team
    Learning cost

    View Slide


  28. Learning curve very smooth

    On doubt, use Java style and methods

    Not a real paradigm change

    Same libraries and concepts
    Learning cost ® BUT...

    View Slide


  29. Good documentation

    Short library

    Is there a general interest in Kotlin in the company?
    Kotlin community
    Android devs pushing for a change
    Learning cost ® BUT... (2)

    View Slide


  30. It's production!

    New technology, may have bugs

    Adds code “behind the courtains”

    Any weird behaviour affecting memory, garbage collection...
    Can affect Performance

    View Slide


  31. Same Java VM

    You can see compiled bytecode

    Decompile problematic code to Java and tune it

    Start a component in Java and then convert it to Kotlin
    Can affect Performance ® BUT...

    View Slide


  32. Extension functions are just a trick, no overhead

    Null checks are a small runtime overhead

    Platform and base libraries are the sane

    Kotlin library overhead not important for backend
    Can affect Performance ® BUT... (2)

    View Slide


  33. Increased build time

    Find equivalent tools

    Adaptation to those tools
    Tooling problems

    View Slide


  34. Same IDE: IntelliJ, Eclipse...

    Same build tools: Maven, Gradle...

    In a microservices architecture build time not so critical
    Seconds?
    Tooling problems ® BUT...

    View Slide


  35. What if Kotlin stops being “cool”?

    What if nobody uses it anymore?

    What if it just dissapears?
    Supported / created by Jetbrains
    Long-term vision

    View Slide

  36. Long-term vision ® BUT...

    View Slide

  37. 03 10 MONTHS
    LATER...

    View Slide


  38. Development time basically the same

    Code Reviews more interesting!

    Our Java platform was 100% compatible
    Learning cost?

    View Slide


  39. Rest of the company?
    4 teams doing services in Kotlin
    Kotlin now official in the company for Android
    Learning cost?

    View Slide

  40. final vs. open
    Some Java libraries rely on
    dinamically creating subclasses

    Interceptors and other injection “black magic”
    Spring, Guice…

    Mocks: Mockito, Spock

    View Slide

  41. Compiler plugins

    All-open: Make classes open
    Shortcut: Spring

    No-args: Create a no-args constructor
    Shortcut: JPA
    Beware!: only by annotations

    View Slide

  42. Compiler plugins ® All-open not enough

    @Transactional problem (if no class annotation)
    Explicit open class and methods

    Mocks?
    Mockito 2.1.0 “inline mocks”: “incubating”
    Kotlin-runner library: explicit packages

    View Slide

  43. Compiler plugins ® No-args not enough

    Object Mapper libraries
    Explicit no-args constructor

    Spock tests
    Named parameters don’t work from Java / Groovy

    View Slide

  44. const val ONE = 1 // MyObject(1) NO OBJECTS ALLOWED
    // Translated to: public static final
    class Constants {
    companion object {
    const val TWO = 2 // MyObject(2) NO OBJECTS ALLOWED
    // Translated to: inlined
    val THREE = MyObject(3) // Translated to private static
    // + Companion class with getter
    @JvmField val FOUR = MyObject(4) // Translated to public static final
    }
    }
    Constants: too many options?
    https://blog.egorand.me/where-do-i-put-my-constants-in-kotlin/

    View Slide

  45. val description: String (type after identifier)

    View Slide

  46. val description: String (type after identifier)

    View Slide

  47. Can affect Performance?

    View Slide


  48. Eclipse with Kotlin and Groovy tests just don’t work

    Eclipse incremental compilation a bit slow

    Some crashes when updating IntelliJ plugin

    Incremental build disabled by default in some versions
    Maven+Gradle

    Checkstyle, Findbugs…
    Tooling problems?

    View Slide


  49. Eclipse with Kotlin and Groovy tests just don’t work

    Eclipse incremental compilation a bit slow

    Some crashes when updating IntelliJ plugin

    Incremental build disabled by default in some versions
    Maven+Gradle

    Checkstyle, Findbugs…
    Tooling problems?

    View Slide

  50. 04 Beyond
    the basics

    View Slide

  51. Functional features
    fun processPersonName(person: Person?, processor: (String) -> Boolean) =
    processor(
    when(person) {
    null -> "null"
    is AnonymousPerson -> "anonymous"
    else -> when {
    person.isBoss -> "a boss"
    else -> "the simple ${person.name}"
    }
    }
    )

    View Slide


  52. Not 100% functional

    Higher kind types

    Typeclasses

    Proposal (KEEP) in discussion to add them, by @raulraja

    Kategory library - http://kategory.io/
    Functional paradigm?

    View Slide

  53. Black magic
    companion { [constants: USER_ID, PRODUCTS, OK_RESULT...) }
    val storeCatalogStub = Stub {
    getProductsForUserId(USER_ID) <= PRODUCTS
    }
    val storeMock = Mock()
    val purchaseHistoryMock: PurchaseHistory = Mock()
    fun `Purchase correct product available in the store`() {
    given("A valid user") {
    userService.isValidUser(USER_ID) <= true
    } `when`("The user tries to do the purchase") {
    purchaseOperation.perform(USER_ID, PRODUCT_ID)
    } then("Product is purchased") {
    1 * storeMock.purchaseProduct(USER_ID, PRODUCT_ID, null.not()) <= OK_RESULT
    } and("Operation is logged") {
    (1..any) * purchaseHistoryMock.logProductPurchased(USER_ID, PRODUCT_ID, any())
    } and("No more interactions") {
    0 * any
    }
    }

    View Slide

  54. Black magic
    companion { [constants: USER_ID, PRODUCTS, OK_RESULT...) }
    val storeCatalogStub = Stub {
    getProductsForUserId(USER_ID) <= PRODUCTS
    }
    val storeMock = Mock()
    val purchaseHistoryMock: PurchaseHistory = Mock()
    fun `Purchase correct product available in the store`() {
    given("A valid user") {
    userService.isValidUser(USER_ID) <= true
    } `when`("The user tries to do the purchase") {
    purchaseOperation.perform(USER_ID, PRODUCT_ID)
    } then("Product is purchased") {
    1 * storeMock.purchaseProduct(USER_ID, PRODUCT_ID, null.not()) <= OK_RESULT
    } and("Operation is logged") {
    (1..any) * purchaseHistoryMock.logProductPurchased(USER_ID, PRODUCT_ID, any())
    } and("No more interactions") {
    0 * any
    }
    }
    Lambda param after
    parenthesis
    Code generation
    (compiler plugin)

    View Slide

  55. Black magic
    companion { [constants: USER_ID, PRODUCTS, OK_RESULT...) }
    val storeCatalogStub = Stub {
    getProductsForUserId(USER_ID) <= PRODUCTS
    }
    val storeMock = Mock()
    val purchaseHistoryMock: PurchaseHistory = Mock()
    fun `Purchase correct product available in the store`() {
    given("A valid user") {
    userService.isValidUser(USER_ID) <= true
    } `when`("The user tries to do the purchase") {
    purchaseOperation.perform(USER_ID, PRODUCT_ID)
    } then("Product is purchased") {
    1 * storeMock.purchaseProduct(USER_ID, PRODUCT_ID, null.not()) <= OK_RESULT
    } and("Operation is logged") {
    (1..any) * purchaseHistoryMock.logProductPurchased(USER_ID, PRODUCT_ID, any())
    } and("No more interactions") {
    0 * any
    }
    }
    Operator overloading
    Reified generics
    Extension functions
    https://github.com/Koxalen

    View Slide

  56. More...

    Coroutines

    Type aliases

    Sealed classes

    Delegated properties

    Inline functions

    View Slide

  57. Android

    Anko library

    Consider performance and limits
    7k methods (65k methods limit): 10%
    380 KB
    Compilation slower

    View Slide

  58. Multi-platform

    Javascript
    Kotlin wrapper for ReactJS

    Native
    Kotlin plugin for CLion
    Kotlin/Native iOS support
    Not only the JVM!
    KotlinConf app fully made with Kotlin

    View Slide

  59. SO WHAT???
    05

    View Slide

  60. Yay or Nay?

    Controlled risk, but… worth it?

    What’s the benefit?
    Mostly syntatic sugar
    Productivity really improved???

    View Slide

  61. Yay or Nay?

    Controlled risk, but… worth it?

    What’s the benefit?
    Mostly syntatic sugar
    Productivity really improved???
    How about thinking about PEOPLE
    instead of PRODUCTS?

    View Slide

  62. It’s all about LEARNING
    and MOTIVATION

    View Slide

  63. View Slide

  64. Questions?
    Andrés Viedma · @andres_viedma

    View Slide