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

エムスリーでのKotlinへの取り組み / jjug-ccc-2018fall-kotlin-in-m3

M3 Engineering
December 15, 2018

エムスリーでのKotlinへの取り組み / jjug-ccc-2018fall-kotlin-in-m3

JJUG CCC 2018 Fallで発表した「エムスリーでのKotlinへの取り組み」の資料です。

M3 Engineering

December 15, 2018
Tweet

More Decks by M3 Engineering

Other Decks in Technology

Transcript

  1. Copyright © 2018 M3, Inc. All Rights Reserved
    エムスリー
    Kotlinへ 取り組み
    エムスリー株式会社
    滝安純平 星川貴樹
    @JJUG CCC 2018 Fall #jjug_ccc #ccc_a4

    View full-size slide

  2. スピーカー紹介
    滝安純平 @juntaki
    サーバーサイド / Webフロントエンドエンジニア
    星川貴樹 @oboenikui
    Android / セキュリティエンジニア

    View full-size slide

  3. エムスリーとは

    View full-size slide

  4. m3.comはこんなサービス
    医師
    主 無料 コンテンツを提供
    ● 医療関連ニュース
    ● 薬剤情報
    ● 講演会動画
    仕事 合間 手軽 情報収集
    きる
    製薬会社等
    有償 サービスを提供
    ● 医師へ 薬剤情報・講演
    会動画 発信
    ● 医師向けアンケート(ニー
    ズ把握)
    低コスト 医師 営業活動が
    きる

    View full-size slide

  5. 目次
    1. Kotlinとエムスリー
    2. サーバサイドの取り組み
    3. Androidの取り組み
    Androidエンジニア? Kotlin使 いる?

    View full-size slide

  6. Kotlinとエムスリー

    View full-size slide

  7. Kotlinとエムスリーの歴史
    社内で多くのプロジェクトがKotlinを採用しています!
    2016
    2017
    2018
    製薬プロモAndroidアプリをKotlin リニューアル開始
    医師転職事業リニューアル、サーバーサイドKotlin導入
    薬剤師転職事業リニューアル
    製薬プロモーション事業リニューアル
    新アンケート管理システム
    …… 新規サーバサイドKotlin続々導入中
    新規AndroidアプリもKotlin 開発中

    View full-size slide

  8. 社内エンジニアチームの紹介
    ● エンジニアは事業部門ごとのチームに所属
    ● いくつかの横断チームもある
    事業
    部門

    横断
    製薬
    プロモ
    新領域
    サイト
    プロモ
    転職事業 リサーチ 治験
    コンシュー

    モバイル
    アプリ
    SRE セキュリティ AI機械学習
    基盤
    電子
    カルテ
    QA

    View full-size slide

  9. Kotlinを使ってるチーム
    サーバーサイド・モバイルアプリ開発によらず使われている
    製薬
    プロモ
    新領域
    サイト
    プロモ
    転職事業 リサーチ 治験
    コンシュー

    事業
    部門

    横断 モバイル
    アプリ
    SRE セキュリティ AI機械学習
    基盤
    電子
    カルテ
    QA

    View full-size slide

  10. 製薬
    プロモ
    新領域
    サイト
    プロモ
    転職事業 リサーチ 治験
    コンシュー

    モバイル
    アプリ
    SRE セキュリティ AI機械学習
    基盤
    電子
    カルテ
    QA
    Kotlinを選んだ主な理由
    事業
    部門

    横断
    Androidアプリ開発メンバが
    サーバーサイドを作る
    Javaエンジニアが多いチーム
    BetterJava し
    エンジニア し 新しいも
    挑戦したい
    他 プロジェクト も使 いた
    ため

    View full-size slide

  11. 技術選定の方針
    チーム・システムごとに所属するエンジニアが自由に選定
    1. メンバーの習熟度
    2. 利用できるライブラリ
    3. 既存システムとの連携

    View full-size slide

  12. サーバーサイドの取り組み

    View full-size slide

  13. サーバーサイドいろいろ
    ● JavaからKotlinに移行する方法
    ○ いろいろな移行方法
    ○ 移行時に気をつけること
    ○ リグレッションテストで品質を担保
    ● サーバーサイドKotlinで採用している要素技術紹介
    ○ SpringBoot
    ○ Swagger
    ○ GraphQL
    エムスリー あ た実際 りくみから、そ ノウハウを紹介します!

    View full-size slide

  14. JavaからKotlinへの移行
    3パターンくらいの方針が取れる
    徐々に変えていく
     新しく作ったものだけKotlinで書いて混在させるなど
    なかったことにする
     リニューアルと銘打って再設計
    全部まとめて書き換える
     IntelliJで単純に自動コンバートして、なんとかする

    View full-size slide

  15. 徐々に変えていく
    8割完成済み javaプロジェクト kotlinを途中導入した話
    https://speakerdeck.com/kawamataryo/8ge-wan-cheng-ji-mifalsejavapuroziekutonikotlinwotu-zhong-dao-ru-sitahua

    View full-size slide

  16. なかったことにする
    10年前 レガシーシステムをサーバーサイド Kotlin フルリニューアルし いる話
    https://speakerdeck.com/maeharin/10nian-qian-falseregasisisutemuwosabasaidokotlindehururiniyuar
    usiteiruhua-number-jjug-ccc-number-ccc-g2
    入社し 最初 プロジェクト

    View full-size slide

  17. 全部まとめて書き換える
    Kotlinらしいコードにはならないが、Kotlinにはなる
    手動でのリファクタリングは必要
    →開発が活発な段階で入れないと厳しい
    https://pleiades.io/help/idea/converting-a-java-file-to-kotlin-file.html

    View full-size slide

  18. 全部まとめて書き換える、ときのノウハウ
    ● 他の機能開発と調整をする(チームでの開発の場合)
    ● リグレッションテストをたくさん作っておく

    View full-size slide

  19. 他の機能開発と調整する
    コンバート前後だとファイル(.java/.kt)が違うため、
    すべて差分になり、機能追加を見つけ出すことが難しい
    コンバート中は機能開発ストップする
    ここ
    コンバート
    しかやら い
    開発再開!!

    View full-size slide

  20. 他の機能開発と調整する
    なるべく短期間で終わらせるため
    コンバート以外の余計なことをやらない
    ● テストコードは一旦Javaのまま
    ● Kotlinらしいコードにするのは機能開発と並行して
    るべく短く!
    だんだんKotlinらしい
    コード すれ 良い

    View full-size slide

  21. リグレッションテスト
    自動コンバートをコードレビューしても仕方ない
    品質の担保はリグレッションテストでカバーする
    “Golden file testing”を採用し テストを量産

    View full-size slide

  22. Golden file testingとは
    APIのレスポンスとGolden fileを比較して、
    レスポンスが変わらないことを確認する
    テスト対象:
    Application層
    ハンドラ
    テストコード
    レスポンスを検査
    Golden file (=JSON)
    Response
    Object
    Request
    Object
    自動生成
    全テスト共通
    ここだけ、書く必要がある
    DB Mock
    データ

    View full-size slide

  23. fun assertEqualOrSaveJsonFile(
    actual: Any,
    javaClass: Class,
    fileName: String
    ) {
    val javaClassPath = javaClass.name.replace(".", "/")
    val path = "src/test/resources/testdata/$javaClassPath/$fileName.json"
    val expected = TestForApplication::class.java.getResource(path)?.readText()
    if (expected == null) {
    saveToJsonFile(path, actual)
    } else {
    assertEqual(expected, actual)
    }
    }
    Golden fileを生成するコード
    Golden fileが
    ● なかったら そのままJSONを書き込んで、例外投げる
    ● あったら ファイルの中身とJSONを比較
    javaClass ディレクトリ
    構成も自動 決める

    View full-size slide

  24. ふつうのテストとの違い
    リグレッションテストに向いている
    よくあるケース Golden file testing ふ う テスト
    テストを増やそう 普通 ファイル生成 assert地獄、あきらめる 漏れる
    出力が変わる変更をした 消し 、普通 ファイル生成 assertを おし いく地獄
    巨大 出力を扱う 普通 ファイル生成 Method code too large!
    JSON文字列をチェック 普通 ファイル生成 \”エスケープが必要\”
    テスト 仕様を明示したいケース 向い い い 、使い分け 大事
    日付や、ランダム 生成し いる
    IDが含まれるよう 場合 、モック等 対応が必要

    View full-size slide

  25. サーバサイドで使っている要素技術
    ● SpringBoot
    ● Swagger
    ● GraphQL

    View full-size slide

  26. SpringBoot
    新規APIで採用するフレームワークはほぼSpringBoot
    Spring Boot 一般ライブラリ 折り合い けかた
    https://speakerdeck.com/saiya_moebius/spring-boot-to-ban-raiburarifalsezhe-rihe-ifalsetukekata

    View full-size slide

  27. SpringBootはハマると難しい・・
    Kotlin+SpringBootなプロジェクトが他にもあると、
    そのまま構成を参考にできたり、聞ける人が多い

    View full-size slide

  28. SpringBootはAPIまで
    フロントエンドはReactやVue.jsを使いたい
    インタラクションをどうする???

    View full-size slide

  29. Swagger Codegen, UI (OpenAPI)
    Kotlin+SpringBootでサーバサイドを作ったら
    ● クライアントライブラリは自動生成できる(Codegen)
    ● ドキュメントも自動生成できる(UI)
    Swagger Codegen APIクライアントgem 自動生成 #m3kt
    https://speakerdeck.com/juntaki/swagger-codegende-apikuraiantogem-zi-dong-sheng-cheng-number-m3kt
    生成されたドキュメント

    View full-size slide

  30. GraphQL
    Swagger(OpenAPI)と似たメリットがある
    新サービスで導入して、ノウハウを蓄積中
    ● クライアントの自動生成
    ● ドキュメントの管理を実装と差分無く
    ● スキーマファーストな開発
    ● 複数のリクエストをまとめる
    フロントエンド向けの API サーバリニューアルに GraphQL を検討している話
    https://www.m3tech.blog/entry/graphql-on-spring-boot-with-kotlin

    View full-size slide

  31. Androidの取り組み

    View full-size slide

  32. Androidいろいろ
    ● チームの紹介
    ○ メンバー構成
    ○ 導入の経緯
    ○ 社内制度
    ● Android周りのKotlin導入時の懸念点
    ○ Optional or Nullable
    ○ Lombok or data class
    ● チームメンバーへの浸透

    View full-size slide

  33. モバイルアプリチームについて
    ● モバイルアプリエンジニアはAndroid, iOS合わせて
    1チームに集中
    ● 自称Kotlinエバンジェリストの長澤太郎さん(通称た
    ろうさん)が今年4月まで在籍(現在はフェロー)
    アプリチーム
    エンジニア
    Kotlin
    エンジニア
    4 → 10
    2 → 7

    View full-size slide

  34. 弊社Androidアプリ
    MR君アプリ
    ● 弊社で最初に作られたMR君と
    いうサービス専用のアプリ
    ● 製薬プロモーション専用
    ● その分m3.comアプリにはな
    い機能がある
    m3.comアプリ
    ● 現在の主要アプリ
    ● MR君を含む主要なサービスが
    1まとめになっているアプリ
    + 未公開新規アプリ 2

    View full-size slide

  35. 導入の経緯
    ● 2016年1月より、たろうさんがOKRとしてMR君アプリ
    のリニューアルを開始
    ● 当時他社でも採用実績がほぼなかったKotlinを採用
    ○ 自称エバンジェリストなので
    ○ 最初に使ったKotlinバージョンは1.0.0-beta-4584
    ○ Google I/OでKotlin正式サポートが発表される前から
    Kotlin化したアプリをリリース

    View full-size slide

  36. OKRについて
    ● “Objectives and Key Results”
    ● Intel発祥、Googleでも1999年から利用
    ● 「目的」を設定し、それを達成するための「目標」を
    定義する
    ○ Objectiveは必ずしも定量的で無くても良い
    ○ Key Resultは定量的で、客観的に判断できる
    ● (大きな目標)
    ● 弊社の場合
    ○ 2013/11からやっている
    ○ チームごとに目標設定
    ○ 目標はリファクタや新規プロダクトなど、短期的にビジネスに
    繋がらなくても良い

    View full-size slide

  37. 2018年現在のAndroid×Kotlin
    ● Google公式サポート (2017~)
    ● 活発なライブラリはほとんどKotlin対応されている
    ● Android公式ドキュメントもKotlin, Javaが併記さ
    れているものが増えている
    ● Android OSのフレームワーク自体はJavaで書かれて
    いるが、Android 9から@NotNull, @Nullableアノ
    テーションがついたことでよりKotlinフレンドリーに
    ● 勉強会やブログ記事でもKotlinが主流

    View full-size slide

  38. 弊社アプリのKotlin対応の歴史
    2016/1 2017/5 2018/6
    Kotlinで
    フルリニューアル
    徐々に書き換え
    初めから
    Kotlinで
    新規
    アプリ
    Java Kotlin

    View full-size slide

  39. 弊社アプリのKotlin対応の歴史
    2016/1 2017/5 2018/6
    Kotlinで
    フルリニューアル
    徐々に書き換え
    初めから
    Kotlinで
    2017/5/16
    フルKotlinで
    リリース!
    新規
    アプリ
    Java Kotlin

    View full-size slide

  40. 弊社アプリのKotlin対応の歴史
    2016/1 2017/5 2018/6
    Kotlinで
    フルリニューアル
    徐々に書き換え
    2017/5/18
    Google I/O 2017で
    AndroidのKotlin公式
    サポート発表
    初めから
    Kotlinで
    新規
    アプリ
    Java Kotlin

    View full-size slide

  41. 弊社アプリのKotlin対応の歴史
    2016/1 2017/5 2018/6
    Kotlinで
    フルリニューアル
    徐々に書き換え
    初めから
    Kotlinで
    2018/6
    開発合宿で新アプリ作成
    (2泊3日)
    新規
    アプリ
    Java Kotlin

    View full-size slide

  42. 弊社アプリKotlin化の現状
    フルリプレイス済(2016~2017)
    ● 前述のリニューアル by たろうさん
    部分的に変えていく方針(2018~)
    ● 新しく作ったクラスについてKotlinで書
    いていく
    ● 大きく変更が入るクラスもKotlin化して
    から編集
    ● data classへの移行などに応じて関連ク
    ラスもKotlin化

    View full-size slide

  43. 書き換えの手間
    余分に工数・手間が発生するために敷居が上がる
    ○ 大幅に書き方の変わるLombok → data class移行
    ○ よりKotlinらしいコードへの書き換え
    etc…
    チームのOKRの目標としてKotlin化を定める
    勉強会などでKotlinの利便性、楽しさを布教

    View full-size slide

  44. Javaとの共存の問題
    ● 使用するクラス、ライブラリの違い
    ○ Optional or Nullable
    ○ Lombok or data class
    ● コンパイル順の問題

    View full-size slide

  45. Optional in Java
    ● Javaでnullは扱いづらいのでOptionalを用いたい
    ● 弊チームのAndroidアプリは、フレームワークやライ
    ブラリの仕様上必要な場合を除き、Optionalを使うな
    どをしていて、全くnullがコードに出現しない
    ● Android 6までOptionalがなかったので、Guavaの
    Optionalを使っている
    String text = Optional.fromNullable(nullableInt)
    .transform(Integer::toString)
    .or("");

    View full-size slide

  46. Nullable in Kotlin
    ● KotlinではNullableをうまく扱う仕組みがある
    (Null安全)
    ● 余計なインスタンスを生成しない分Optionalよりも
    オーバーヘッドが少ない
    val text = nullableInt?.toString() ?: ""

    View full-size slide

  47. Optional in Kotlin
    ● GuavaのOptionalは正直使い勝手が悪い
    ○ nullになるはずのない部分が@Nullableになっていたり
    ○ ラムダを渡してもinline展開されなかったり
    ○ 代替案としてはArrowというFPライブラリののOptionがある
    ● orNull()を呼べば、Kotlin上ではNullable型とし
    て処理可能
    // Optionalのまま処理
    val text = intOptional
    .transform { it!!.toString() } // なぜかNullable
    .or("")
    // Nullableに変換
    val text = intOptional.orNull()?.toString() ?: ""

    View full-size slide

  48. Optional or Nullable
    チーム内コーディング規約
    ● Javaから呼び出されるKotlinクラスのpublicメソッ
    ドはOptionalでやりとりする
    ● Kotlin内で閉じている場合はNullableにしてOK
    ● RxJavaではnullを流せないので、どうしても使う場
    合のみOptional
    ○ Single>はケースによってはMaybeに
    ○ コルーチンはJava側から触れないので

    View full-size slide

  49. Lombok or data class
    ● JavaではLombokを使用していた
    ● Kotlinにはdata classという、自動でequals,
    hashCodeメソッドを作ってくれる機能がある
    data class User(
    val name: String,
    val sex: Sex,
    val age: Int,
    val prefectureCode: Int
    )
    @Value
    @Builder
    @Wither
    public class User {
    @NonNull String name;
    @NonNull Sex sex;
    int age;
    int prefectureCode;
    }

    View full-size slide

  50. Lombok or data class
    ● Kotlinでは、Builderパターンなどの代わりに名前付
    き引数を用いる
    User.builder()
    .name("oboenikui")
    .sex(Sex.MAN)
    .age(25)
    .prefectureCode(12)
    .build();
    val user = User(
    name = "oboenikui",
    sex = Sex.MAN,
    age = 25,
    prefectureCode = 12
    )

    View full-size slide

  51. Lombok or data class
    ● Kotlinでは、Builderパターンなどの代わりに名前付
    き引数を用いる
    user.withAge(
    user.getAge() + 1
    );
    user.copy (
    age = user.age + 1
    )

    View full-size slide

  52. Javaからdata classの呼び出し
    val user = User(
    name = "oboenikui",
    sex = Sex.MAN,
    age = 25,
    prefectureCode = 12
    )
    User user = new User(
    "Takaki Hoshikawa",
    Sex.MAN,
    25,
    12
    );
    年齢?
    県コード?
    ビルダーや名前付き引数の機能がないので
    コンストラクタなどに渡す変数がわかりにくい

    View full-size slide

  53. Javaからdata classの呼び出し
    user.copy (
    age = user.age + 1
    )
    user.copy(
    user.getName(),
    user.getSex(),
    user.getAge() + 1,
    user.getPrefectureCode()
    );
    名前付き引数がないので
    copyメソッドが使いにくい

    View full-size slide

  54. KotlinからLombokの呼び出し
    User.builder()
    .name("oboenikui")
    .sex(Sex.MAN)
    .age(25)
    .prefectureCode(12)
    .build()
    User.builder()
    .name("oboenikui")
    .sex(Sex.MAN)
    .age(25)
    .prefectureCode(12)
    .build();
    ERROR
    LombokとKotlinは同じモジュール内で共存できない
    (モジュールを分けたりDelombokする必要がある)

    View full-size slide

  55. その他
    User.builder()
    // .name("oboenikui")
    .sex(Sex.MAN)
    .age(25)
    .prefectureCode(12)
    .build();
    val user = User(
    // name = "oboenikui",
    sex = Sex.MAN,
    age = 25,
    prefectureCode = 12
    )
    引数が足りないので
    シンタックスエラー
    build()実行時にエラー
    Builderパターンはdata classに比べて厳密にならない

    View full-size slide

  56. data classとLombokの使用方針
    ● 他のクラスより慎重にKotlin化する
    ○ 多くの箇所で生成するものは後回しにする
    ○ data class化したら、使っているクラスも合わせてKotlin

    ○ 利用するwitherのみ定義する
    ○ JavaのUTで使う場合はコメントを入れる
    data class User(
    /* 略 */
    ) {
    fun withAge(age: Int): User {
    return copy(age = age)
    }
    }

    View full-size slide

  57. data classとLombokの使用方針
    ● 他のクラスより慎重にKotlin化する
    ○ 多くの箇所で生成するものは後回しにする
    ○ data class化したら、使っているクラスも合わせてKotlin

    ○ 利用するwitherのみ定義する
    ○ JavaのUTで使う場合はコメントを入れる
    User testUser = new User(
    "Takaki Hoshikawa", // name
    Sex.MAN, // sex
    25, // age
    12 // prefectureCode
    );

    View full-size slide

  58. data classとLombokの使用方針
    ● Lombokを使うモジュールを隔離 / Delombok
    ○ マルチモジュール化でビルド速度改善も期待できる
    ○ Lombokクラスからはdata classを利用できない
    module A
    (w/ Lombok)
    module B
    (data class)
    main module
    &
    依存方向
    モジュール隔離

    View full-size slide

  59. data classとLombokの使用方針
    ● Lombokを使うモジュールを隔離 / Delombok
    ○ マルチモジュール化でビルド速度改善も期待できる
    ○ Lombokクラスからはdata classを利用できない
    Delombok
    @Value
    @Builder
    @Wither
    public class User {
    @NonNull String name;
    @NonNull Sex sex;
    int age;
    int prefectureCode;
    }
    public class User {
    /* 略 */
    public String getName() {
    return name;
    }
    /* 略 */
    }

    View full-size slide

  60. アプリサイズの増加
    ● kotlin-stdlib (Kotlin独自のメソッドやクラス)
    のサイズは1.1MBある
    ● ProGuard (難読化 & 未使用コード削除) でサイズ
    を減らせる
    ○ m3.comアプリ導入時は約0.1MBの増加に抑えられた

    View full-size slide

  61. メンバーへの浸透
    コードレビュー
    ● Javaを書けるエンジニアであればKotlinの動くコー
    ドを書くことは容易
    ● 「Kotlinらしさ」をLintやコードレビューで学習
    社内Wiki・Slack
    ● コードレビューで指摘した内容を中心に、Wikiに解説
    を含めてまとめる
    ● 細かいスタイルについては積極的にSlackの#kotlin
    チャンネルで議論

    View full-size slide

  62. コーディング規約・Lint
    ● 著名なスタイルガイド2種を踏襲
    ○ JetBrainsスタイルガイド
    ○ Googleスタイルガイド
    ● Lintでスタイルガイドに準拠しているかチェック
    ○ ktlint
    ○ detekt
    ● IntelliJ IDEAのインスペクタ・フォーマッタも
    Kotlin 1.3からスタイルガイドに準拠できるように
    ● スタイルガイドにないものはゆるい規約として社内
    Wikiで共有

    View full-size slide

  63. Android Unit Test
    ● JUnit4 + Robolectric + AssertJ + MockK
    ● バッククォートで囲むことで可読性向上
    @Test
    fun `getFoo should return foo bar`() {
    /* Test code */
    }
    @Test
    public void getFoo_should_return_foo_bar() {
    /* Test code */
    }

    View full-size slide

  64. 外部発信

    View full-size slide

  65. 勉強会
    次回は2月中旬予定!

    View full-size slide

  66. テックブログ
    Advent Calendarもやってます!
    https://www.m3tech.blog

    View full-size slide

  67. スポンサー活動
    ● JJUG CCC
    ○ ゴールドスポンサー
    ○ コーヒースポンサー
    ○ ブーススポンサー ← NOW!!
    ● Kotlin Fest
    ● ScalaMatsuri
    ● Ruby Kaigi
    ● Vue Fes Japan
    ● DroidKaigi 2019
    ● try! Swift Tokyo 2019 JJUG CCC 2017 Springのコップ

    View full-size slide

  68. We are hiring!

    View full-size slide

  69. We are hiring!
    ● グローバルで働いてみたいエンジニア
    ○ US出張やってます!
    ● Androidエンジニア
    ○ 新規アプリはGraphQLを使って開発中!
    ● サーバーサイドエンジニア
    ○ Spring Boot + Kotlin + GraphQL
    ○ Java, Scalaエンジニアも募集中!
    https://bit.ly/ccc2018m3

    View full-size slide

  70. Thank you for listening!

    View full-size slide