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 Slide

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

    View Slide

  3. エムスリーとは

    View Slide

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

    View Slide

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

    View Slide

  6. Kotlinとエムスリー

    View Slide

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

    View Slide

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

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

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

    View Slide

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

    事業
    部門

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

    View Slide

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

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

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View 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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View 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 Slide

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

    View Slide

  31. Androidの取り組み

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View 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 Slide

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

    View 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 Slide

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

    View 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 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 Slide

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

    View 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 Slide

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

    View 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 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 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 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 Slide

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

    View 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 Slide

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

    View Slide

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

    View Slide

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

    View 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 Slide

  64. 外部発信

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  68. We are hiring!

    View Slide

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

    View Slide

  70. Thank you for listening!

    View Slide