Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

エムスリーとは

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

Kotlinとエムスリー

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

社内エンジニアチームの紹介 ● エンジニアは事業部門ごとのチームに所属 ● いくつかの横断チームもある 事業 部門 ご 横断 製薬 プロモ 新領域 サイト プロモ 転職事業 リサーチ 治験 コンシュー マ モバイル アプリ SRE セキュリティ AI機械学習 基盤 電子 カルテ QA

Slide 9

Slide 9 text

Kotlinを使ってるチーム サーバーサイド・モバイルアプリ開発によらず使われている 製薬 プロモ 新領域 サイト プロモ 転職事業 リサーチ 治験 コンシュー マ 事業 部門 ご 横断 モバイル アプリ SRE セキュリティ AI機械学習 基盤 電子 カルテ QA

Slide 10

Slide 10 text

製薬 プロモ 新領域 サイト プロモ 転職事業 リサーチ 治験 コンシュー マ モバイル アプリ SRE セキュリティ AI機械学習 基盤 電子 カルテ QA Kotlinを選んだ主な理由 事業 部門 ご 横断 Androidアプリ開発メンバが サーバーサイドを作る Javaエンジニアが多いチーム BetterJava し エンジニア し 新しいも 挑戦したい 他 プロジェクト も使 いた ため

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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 ディレクトリ 構成も自動 決める

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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 生成されたドキュメント

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

Androidの取り組み

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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() ?: ""

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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; }

Slide 50

Slide 50 text

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 )

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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 ); 年齢? 県コード? ビルダーや名前付き引数の機能がないので コンストラクタなどに渡す変数がわかりにくい

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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する必要がある)

Slide 55

Slide 55 text

その他 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に比べて厳密にならない

Slide 56

Slide 56 text

data classとLombokの使用方針 ● 他のクラスより慎重にKotlin化する ○ 多くの箇所で生成するものは後回しにする ○ data class化したら、使っているクラスも合わせてKotlin 化 ○ 利用するwitherのみ定義する ○ JavaのUTで使う場合はコメントを入れる data class User( /* 略 */ ) { fun withAge(age: Int): User { return copy(age = age) } }

Slide 57

Slide 57 text

data classとLombokの使用方針 ● 他のクラスより慎重にKotlin化する ○ 多くの箇所で生成するものは後回しにする ○ data class化したら、使っているクラスも合わせてKotlin 化 ○ 利用するwitherのみ定義する ○ JavaのUTで使う場合はコメントを入れる User testUser = new User( "Takaki Hoshikawa", // name Sex.MAN, // sex 25, // age 12 // prefectureCode );

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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; } /* 略 */ }

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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 */ }

Slide 64

Slide 64 text

外部発信

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

We are hiring!

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

Thank you for listening!