Slide 1

Slide 1 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest Server-side
 Kotlinを使う
 スタートアップで
 どんなDetekt
 ルールが育ったか
 Kotlin Fest 2022 Kengo TODA

Slide 2

Slide 2 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest トーク概要
 株式会社ヘンリーではサーバサイドをKotlin(gRPC, Ktor, Koin etc.) で書いています。サービスの品質を高めるため、また拡大期にある チームへのボーディングを簡単にするため、Detekt拡張を実装して 利用しています。
 
 このセッションではどのような課題を解決するためにDetektを利用し たか、その結果どのようにコーディング体験が改善されたかをご紹 介します。
 
 https://fortee.jp/kotlin-fest-2022/proposal/1f737120-934f-48ac-b 6f3-66da69d433d4
 2

Slide 3

Slide 3 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest 自己紹介
 Kengo TODA ( @kengo_toda)
 株式会社ヘンリー SRE
 
 最近の活動
 • @actions/setup-java に依存キャッシュ機能を入れた
 • 〃にGradle Version Catalog対応を入れた
 • detektのgood first issueにあった機能を実装した
 • Terraform周りではtfmigrate & tfactionのGCP対応
 3

Slide 4

Slide 4 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest Detektとは
 Kotlin用静的解析ツール。Javaで言うPMDのように、抽象構文木 (AST)を解析して潜在的な課題を発見できる。
 https://detekt.dev/
 
 
 4

Slide 5

Slide 5 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest 拡大期のスタートアップで
 生まれるコードの課題
 拡大期とはなにか、どのようなコードの課題があるのか
 5

Slide 6

Slide 6 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest 拡大期のスタートアップとは
 プロダクトの方向性が見えてき て、アクセルを踏む時期。
 仮説検証の速度だけではなく、価 値を顧客に届けるスループットも 同時に高める必要がある。
 完全新規の開発に加え、機能拡 張や機能改善の比率も高い。
 6 "調達した資金は、中小病院向けクラウド型電子カルテ・レセコ ンシステムの開発および営業・サポート体制の強化に充当し、 事業拡大に合わせた採用・組織体制の強化を図ってまいりま す。" "年内で組織を2倍に拡大"
 https://prtimes.jp/main/html/rd/p/000000001.000102549.html 


Slide 7

Slide 7 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest エンジニアから見た
 拡大期のスタートアップとは
 採用拡大によるオンボーディング 負荷の増大。
 少数に共有された暗黙知では回 らなくなる=ドメイン知識やチーム への慣れがなくても参入しやすい プロジェクトが必要。
 ”ちょっと汚いけど簡単な変更”の 誘惑。コードがとっ散らかったり負 債が増えたりしがち。
 7

Slide 8

Slide 8 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest 静的解析ツールで解決できる課題
 オンボーディング負荷の低減
 • 「そうも書けますよね、でも今はこの書き方で行こうと思ってます」を 人間がフィードバックしないで済む
 
 機械的に発見可能な負債の抑制
 • 人が気づいていない潜在的課題や「なんとかなるだろ!👿」に対し て機械的に「ダメですよ😇」と言ってくれる
 ”第二のコンパイラ”
 8

Slide 9

Slide 9 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest 自前で実装したExtension例
 電子カルテ開発現場で便利に使えた、あるいは利用を検討している機能
 9

Slide 10

Slide 10 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest Kotlinが提供するrequire, requireNotNull, check, checkNotNullは便利 だが、エラーメッセージがわかりにくい。
 例えばrequireが投げるIllegalArgumentExceptionのメッセージは “Failed requirement” だけで、起こった問題を理解するための情報を 提供しない。
 わかりにくいエラーメッセージの撲滅
 10

Slide 11

Slide 11 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest 本番環境でこのメッセージが確認された場合に、以下のような情報を 得られずにコードを読む必要が生じてしまう。
 ● この例外はユーザにどのような影響があるのか
 ● この例外はどの機能に関連しているのか
 ● この例外について詳しいのはどのチームか
 
 最悪の場合、障害対応の初動が遅れる理由になりかねない。
 わかりにくいエラーメッセージの撲滅
 11

Slide 12

Slide 12 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest わかりにくいエラーメッセージの撲滅
 detekt拡張によって、require, requireNotNull, check, checkNotNullを説 明なしに使うことを抑制する。障害に対する初動を早め、MTTRの短縮 に貢献する。
 requireNotNull(records[item.inspectionCode]) // エラー:assertionを行う場合、Sentryに表示される // ことを念頭にメッセージを設定してください。 requireNotNull(records[item.inspectionCode]) { "検査番号 (${item.inspectionCode}) に対応する院外検査が見つかりません" } // OK 12

Slide 13

Slide 13 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest 導入効果 13 初動を遅くするエラーを本番環境 に入れることがなくなった 同僚のエンジニアからも好評

Slide 14

Slide 14 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest 実装は単純なので試してみてください!
 14

Slide 15

Slide 15 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest 日付とタイムゾーン
 誕生日などを表現するときに java.time.LocalDate(年・月・日の 情報を持つクラス)を利用すること がある。
 これを時刻すなわち java.time.Instantに変換する場 合、どのタイムゾーンを使うかが 重要になる。
 
 15

Slide 16

Slide 16 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest 日付とタイムゾーン
 例えば localDate.atStartOfDay() は実行環境のデフォルト タイムゾーンを使う。
 実行環境依存の実装は予期しない挙動の原因となるため、コードの 可搬性を保つためにも、デフォルトタイムゾーンに依存しない事が必 要になる。
 
 ※Java18でデフォルトのCharsetが「UTF-8」になるまでは、文字コード に対しても同様の配慮が必要だった。
 16

Slide 17

Slide 17 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest 「今日」を表すLocalDateの生成
 LocalDate.now() // NG, デフォルトタイムゾーンに依存 LocalDate.now(jstZoneId) // OKだが書き方統一のため避ける Instant.now().toJstLocalDate() // OK, Instantに拡張関数を追加して共通化 17

Slide 18

Slide 18 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest LocalDateからInstantへの変換
 localDate.atStartOfDay().toInstant() // NG, デフォルトタイムゾーンに依存 localDate.atStartOfDay(jstZoneId).toInstant() // OKだが書き方統一のため避ける localDate.toJstStartOfDayInstant() // OK, Instantに拡張関数を追加して共通化 18

Slide 19

Slide 19 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest java.time.Clockの利用を推進
 Instant.now() // NG, 単体テスト実行時に不便 clock.instant() // OK, コンストラクタを通じて得たClockを利用 // kotestを使うならkotest-extensions-clockが便利 19

Slide 20

Slide 20 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest Repositoryのメソッドの命名を統一
 repository.get(...) repository.list(...) repository.load(...) // NG, find or findByXxx に統一 repository.find(...) repository.findByDoctor(...) // OK 20

Slide 21

Slide 21 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest IDの扱い
 ID系クラスを自動生成し、扱いに統一的なルールを設ける。 class UserRecord(id: EntityID) : UUIDEntity(id) { companion object : UUIDEntityClass(Users) // ExposedのRecordでPKを参照するには.entityIdを使う val entityId = UserId.of(id.value) // FKを参照するときは拡張関数を使う val doctorId by Sessions.doctorUuid .transformToEntityId(UserId.Doctor::of) 21

Slide 22

Slide 22 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest まとめ
 Server-side Kotlinを使うスタートアップでどんなDetektルールが育ったか
 22

Slide 23

Slide 23 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest 積み上げた「ひと工夫」を伝えるルール 23 知っていてほしいことをREADMEにまとめたり新人向け資料に盛り込 んだりすることも大切ですが、コンピュータの力を借りてオンボーディ ングをスケーラブルにすることもできる。 コードの質が底上げされ、エラーメッセージやログが改善されれば、運 用も次第にやりやすくなる。チーム拡大はもちろん、SRE観点からも 価値ある施策だと言える!

Slide 24

Slide 24 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest 小ネタ
 Detektを使うときに考えておいたほうがよいこと
 24

Slide 25

Slide 25 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest Detektの実行速度
 コンパイルやテスト実行に比べれば、Detektの実行時間は小さい。現 時点ではビルドのボトルネックになってはいない。
 
 とはいえコードや適用ルールが増えてくればこの限りではないので、 他のタスクと並列実行できるような工夫(後述)も必要になるかもしれ ない。
 25

Slide 26

Slide 26 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest フォーマットをDetektに管理させるか?
 Detektにはktlintベースのルールが提供されている:
 https://detekt.dev/docs/rules/formatting/
 
 一方で、フォーマットはktlint, ktfmtといった自動フォーマッタに任せて Detektを利用しないという手もある。採用した自動フォーマッタとの噛 み合わせが悪い場合や、GitHub Actionsにフォーマット適用させたい 場合などは、Detektを利用しないほうが良いかも。
 26

Slide 27

Slide 27 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest Detekt以外の解決方法
 主に依存関係の課題であれば、Detektや自動フォーマッタ以外にも解 決に使える手段がいくつかある。 • Gradleプロジェクトをマルチプロジェクト構成にすることで不要な依 存を見えなくする
 
 • ArchUnitでパッケージ間依存に制限をかける
 27

Slide 28

Slide 28 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest マルチプロジェクト構成の利点
 28 例えばexposedがinfrastructure プロジェクトの実装上の依存であ りAPIとして露出していない場合、 infrastructureを利用する applicationからはexposedが見 えない。 domainプロジェクトがktorや exposed、 openapiなどのライブ ラリを見ることもなくなる。

Slide 29

Slide 29 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest マルチプロジェクト構成の利点
 29 Gradleはプロジェクトごとにタスク を実行する。つまりプロジェクトの 数だけタスクを並列実行する可能 性が出てくる。 マルチプロジェクト構成にすること でCPUコアを効率的に使いビルド 時間を短縮できる可能性がある。 Kotest実行 ドキュメント作成 Kotlinコンパイル

Slide 30

Slide 30 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest マルチプロジェクト構成の利点
 30 また若干変則的だが、Detektを 実行するGradleタスクを(比較的 暇な)ルートプロジェクトに置くこと で、並列性を高められる場合があ る。 Kotest実行 ドキュメント作成 Kotlinコンパイル root Detekt実行

Slide 31

Slide 31 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest Detekt extensionをPackagesで配布
 自前実装したExtensionを配布する方法としてPackagesを利用した。こ のとき各プロジェクトにPackagesから依存をダウンロードするための設 定を書くと複雑になるのを避けるため、Wantedly社の事例を参考に Gradle Pluginを実装して設定を簡素化した。
 https://github.com/bw-company/henry-maven-repository
 
 31

Slide 32

Slide 32 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest ご参加ありがとうございました!
 32

Slide 33

Slide 33 text

Copyrights(c) Henry, Inc. All rights reserved. #kotlinfest We are Hiring !
 
 ● 医療DX
 ● 難しい課題の解決
 ● 社会貢献
 
 これらのキーワードが気になる方、
 ぜひ一度カジュアルにお話ししましょう!
 
 採用サイト
 https://jobs.henry-app.jp/
 
 33