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. 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("<empty>") } } ”My billion dollars mistake” Tony Hoare
  2. fun printStringLength1(maybeString: String?) { maybeString?.let { s -> println(s) }

    } fun printStringLengthOrEmpty(maybeString: String?) { println(maybeString?.length ?: "<empty>") } Null safety
  3. A small immutable Kotlin class data class TokenInfo( val tokenType:

    String = “auth”, val identity: String val expiration: Int? = null )
  4. 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; } }
  5. 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; } }
  6. 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
  7. 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
  8. Collections improvements List<Person> persons = Arrays.asList( new Person("Sansa", "Stark"), new

    Person("Jon", "Snow")); List<String> 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
  9. Collections improvements List<Integer> result = list.stream() .flatMap(o -> o.isPresent() ?

    Stream.of(o.get()) : Stream.empty()) .collect(Collectors.toList()); val result = list.filter { it != null } List<Integer> result = list.stream() .flatMap(o → o.map(Stream::of) .orElseGet(Stream::empty)) .collect(Collectors.toList()); List<Integer> result = list.stream() .flatMap(Optional::stream) .collect(Collectors.toList());
  10. String Interpolation List<Person> persons = Arrays.asList( new Person("Sansa", "Stark"), new

    Person("Jon", "Snow")); List<String> 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}" }
  11. Type Inference val persons = listOf( Person("Sansa", "Stark"), Person("Jon", "Snow"))

    val personNames = persons .map { p -> "${p.name} ${p.surname}" } List<Person> persons = Arrays.asList( new Person("Sansa", "Stark"), new Person("Jon", "Snow")); List<String> personNames = persons.stream() .map(p -> p.getName() + " " + p.getSurname()) .collect(Collectors.toList());
  12. Tell the team! The conformist The idealist The responsible AGGG

    ROOO RAGG AGGG ROOO RAGG ROOOOOOOGHH! ROOOOOOOGHH!
  13. • Can affect development time • Misunderstandings can increase bug

    rate • Service responsibility is not fixed Not just YOUR team Learning cost
  14. • Learning curve very smooth • On doubt, use Java

    style and methods • Not a real paradigm change • Same libraries and concepts Learning cost ® BUT...
  15. • 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)
  16. • It's production! • New technology, may have bugs •

    Adds code “behind the courtains” • Any weird behaviour affecting memory, garbage collection... Can affect Performance
  17. • 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...
  18. • 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)
  19. • Same IDE: IntelliJ, Eclipse... • Same build tools: Maven,

    Gradle... • In a microservices architecture build time not so critical Seconds? Tooling problems ® BUT...
  20. • What if Kotlin stops being “cool”? • What if

    nobody uses it anymore? • What if it just dissapears? Supported / created by Jetbrains Long-term vision
  21. • Development time basically the same • Code Reviews more

    interesting! • Our Java platform was 100% compatible Learning cost?
  22. • Rest of the company? 4 teams doing services in

    Kotlin Kotlin now official in the company for Android Learning cost?
  23. final vs. open Some Java libraries rely on dinamically creating

    subclasses • Interceptors and other injection “black magic” Spring, Guice… • Mocks: Mockito, Spock
  24. Compiler plugins • All-open: Make classes open Shortcut: Spring •

    No-args: Create a no-args constructor Shortcut: JPA Beware!: only by annotations
  25. 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
  26. Compiler plugins ® No-args not enough • Object Mapper libraries

    Explicit no-args constructor • Spock tests Named parameters don’t work from Java / Groovy
  27. 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/
  28. • 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?
  29. • 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?
  30. 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}" } } )
  31. • Not 100% functional • Higher kind types • Typeclasses

    • Proposal (KEEP) in discussion to add them, by @raulraja • Kategory library - http://kategory.io/ Functional paradigm?
  32. Black magic companion { [constants: USER_ID, PRODUCTS, OK_RESULT...) } val

    storeCatalogStub = Stub<StoreCatalog> { getProductsForUserId(USER_ID) <= PRODUCTS } val storeMock = Mock<ProductStore>() 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 } }
  33. Black magic companion { [constants: USER_ID, PRODUCTS, OK_RESULT...) } val

    storeCatalogStub = Stub<StoreCatalog> { getProductsForUserId(USER_ID) <= PRODUCTS } val storeMock = Mock<ProductStore>() 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)
  34. Black magic companion { [constants: USER_ID, PRODUCTS, OK_RESULT...) } val

    storeCatalogStub = Stub<StoreCatalog> { getProductsForUserId(USER_ID) <= PRODUCTS } val storeMock = Mock<ProductStore>() 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
  35. More... • Coroutines • Type aliases • Sealed classes •

    Delegated properties • Inline functions
  36. Android • Anko library • Consider performance and limits 7k

    methods (65k methods limit): 10% 380 KB Compilation slower
  37. 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
  38. Yay or Nay? • Controlled risk, but… worth it? •

    What’s the benefit? Mostly syntatic sugar Productivity really improved???
  39. 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?