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

Server-side Kotlinを使うスタートアップでどんなDetektルールが育ったか / Detekt rules made in start-up working with Server-side Kotlin

Kengo TODA
December 10, 2022

Server-side Kotlinを使うスタートアップでどんなDetektルールが育ったか / Detekt rules made in start-up working with Server-side Kotlin

Kotlin Fest 2022で使用した発表資料です。

https://fortee.jp/kotlin-fest-2022/proposal/1f737120-934f-48ac-b6f3-66da69d433d4

Kengo TODA

December 10, 2022
Tweet

More Decks by Kengo TODA

Other Decks in Programming

Transcript

  1. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    Server-side

    Kotlinを使う

    スタートアップで

    どんなDetekt

    ルールが育ったか

    Kotlin Fest 2022
    Kengo TODA

    View Slide

  2. 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

    View Slide

  3. 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

    View Slide

  4. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    Detektとは

    Kotlin用静的解析ツール。Javaで言うPMDのように、抽象構文木
    (AST)を解析して潜在的な課題を発見できる。

    https://detekt.dev/



    4

    View Slide

  5. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    拡大期のスタートアップで

    生まれるコードの課題

    拡大期とはなにか、どのようなコードの課題があるのか

    5

    View Slide

  6. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    拡大期のスタートアップとは

    プロダクトの方向性が見えてき
    て、アクセルを踏む時期。

    仮説検証の速度だけではなく、価
    値を顧客に届けるスループットも
    同時に高める必要がある。

    完全新規の開発に加え、機能拡
    張や機能改善の比率も高い。

    6
    "調達した資金は、中小病院向けクラウド型電子カルテ・レセコ
    ンシステムの開発および営業・サポート体制の強化に充当し、
    事業拡大に合わせた採用・組織体制の強化を図ってまいりま
    す。" "年内で組織を2倍に拡大"

    https://prtimes.jp/main/html/rd/p/000000001.000102549.html

    View Slide

  7. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    エンジニアから見た

    拡大期のスタートアップとは

    採用拡大によるオンボーディング
    負荷の増大。

    少数に共有された暗黙知では回
    らなくなる=ドメイン知識やチーム
    への慣れがなくても参入しやすい
    プロジェクトが必要。

    ”ちょっと汚いけど簡単な変更”の
    誘惑。コードがとっ散らかったり負
    債が増えたりしがち。

    7

    View Slide

  8. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    静的解析ツールで解決できる課題

    オンボーディング負荷の低減

    • 「そうも書けますよね、でも今はこの書き方で行こうと思ってます」を
    人間がフィードバックしないで済む


    機械的に発見可能な負債の抑制

    • 人が気づいていない潜在的課題や「なんとかなるだろ!👿」に対し
    て機械的に「ダメですよ😇」と言ってくれる

    ”第二のコンパイラ”

    8

    View Slide

  9. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    自前で実装したExtension例

    電子カルテ開発現場で便利に使えた、あるいは利用を検討している機能

    9

    View Slide

  10. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    Kotlinが提供するrequire, requireNotNull, check, checkNotNullは便利
    だが、エラーメッセージがわかりにくい。

    例えばrequireが投げるIllegalArgumentExceptionのメッセージは
    “Failed requirement” だけで、起こった問題を理解するための情報を
    提供しない。

    わかりにくいエラーメッセージの撲滅

    10

    View Slide

  11. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    本番環境でこのメッセージが確認された場合に、以下のような情報を
    得られずにコードを読む必要が生じてしまう。

    ● この例外はユーザにどのような影響があるのか

    ● この例外はどの機能に関連しているのか

    ● この例外について詳しいのはどのチームか


    最悪の場合、障害対応の初動が遅れる理由になりかねない。

    わかりにくいエラーメッセージの撲滅

    11

    View Slide

  12. 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

    View Slide

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

    View Slide

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

    14

    View Slide

  15. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    日付とタイムゾーン

    誕生日などを表現するときに
    java.time.LocalDate(年・月・日の
    情報を持つクラス)を利用すること
    がある。

    これを時刻すなわち
    java.time.Instantに変換する場
    合、どのタイムゾーンを使うかが
    重要になる。


    15

    View Slide

  16. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    日付とタイムゾーン

    例えば localDate.atStartOfDay() は実行環境のデフォルト
    タイムゾーンを使う。

    実行環境依存の実装は予期しない挙動の原因となるため、コードの
    可搬性を保つためにも、デフォルトタイムゾーンに依存しない事が必
    要になる。


    ※Java18でデフォルトのCharsetが「UTF-8」になるまでは、文字コード
    に対しても同様の配慮が必要だった。

    16

    View Slide

  17. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    「今日」を表すLocalDateの生成

    LocalDate.now()
    // NG, デフォルトタイムゾーンに依存
    LocalDate.now(jstZoneId)
    // OKだが書き方統一のため避ける
    Instant.now().toJstLocalDate()
    // OK, Instantに拡張関数を追加して共通化
    17

    View Slide

  18. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    LocalDateからInstantへの変換

    localDate.atStartOfDay().toInstant()
    // NG, デフォルトタイムゾーンに依存
    localDate.atStartOfDay(jstZoneId).toInstant()
    // OKだが書き方統一のため避ける
    localDate.toJstStartOfDayInstant()
    // OK, Instantに拡張関数を追加して共通化
    18

    View Slide

  19. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    java.time.Clockの利用を推進

    Instant.now()
    // NG, 単体テスト実行時に不便
    clock.instant()
    // OK, コンストラクタを通じて得たClockを利用
    // kotestを使うならkotest-extensions-clockが便利
    19

    View Slide

  20. 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

    View Slide

  21. 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

    View Slide

  22. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    まとめ

    Server-side Kotlinを使うスタートアップでどんなDetektルールが育ったか

    22

    View Slide

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

    View Slide

  24. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    小ネタ

    Detektを使うときに考えておいたほうがよいこと

    24

    View Slide

  25. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    Detektの実行速度

    コンパイルやテスト実行に比べれば、Detektの実行時間は小さい。現
    時点ではビルドのボトルネックになってはいない。


    とはいえコードや適用ルールが増えてくればこの限りではないので、
    他のタスクと並列実行できるような工夫(後述)も必要になるかもしれ
    ない。

    25

    View Slide

  26. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    フォーマットをDetektに管理させるか?

    Detektにはktlintベースのルールが提供されている:

    https://detekt.dev/docs/rules/formatting/


    一方で、フォーマットはktlint, ktfmtといった自動フォーマッタに任せて
    Detektを利用しないという手もある。採用した自動フォーマッタとの噛
    み合わせが悪い場合や、GitHub Actionsにフォーマット適用させたい
    場合などは、Detektを利用しないほうが良いかも。

    26

    View Slide

  27. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    Detekt以外の解決方法

    主に依存関係の課題であれば、Detektや自動フォーマッタ以外にも解
    決に使える手段がいくつかある。
    • Gradleプロジェクトをマルチプロジェクト構成にすることで不要な依
    存を見えなくする


    • ArchUnitでパッケージ間依存に制限をかける

    27

    View Slide

  28. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    マルチプロジェクト構成の利点

    28
    例えばexposedがinfrastructure
    プロジェクトの実装上の依存であ
    りAPIとして露出していない場合、
    infrastructureを利用する
    applicationからはexposedが見
    えない。
    domainプロジェクトがktorや
    exposed、 openapiなどのライブ
    ラリを見ることもなくなる。

    View Slide

  29. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    マルチプロジェクト構成の利点

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

    View Slide

  30. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    マルチプロジェクト構成の利点

    30
    また若干変則的だが、Detektを
    実行するGradleタスクを(比較的
    暇な)ルートプロジェクトに置くこと
    で、並列性を高められる場合があ
    る。
    Kotest実行
    ドキュメント作成
    Kotlinコンパイル
    root
    Detekt実行

    View Slide

  31. 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

    View Slide

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

    32

    View Slide

  33. Copyrights(c) Henry, Inc. All rights reserved.
    #kotlinfest
    We are Hiring !


    ● 医療DX

    ● 難しい課題の解決

    ● 社会貢献


    これらのキーワードが気になる方、

    ぜひ一度カジュアルにお話ししましょう!


    採用サイト

    https://jobs.henry-app.jp/


    33

    View Slide