🔍

Qodanaによる静的コード解析導入とガイドラインを整備した

2024/12/10に公開

WealthNavi アドベントカレンダー 10日目です。
本日は 入社して1つ目のタスクとして行ったQodanaという静的コード解析の導入とガイドラインを整備した話となります。

Qodanaとは

QodanaはJetBrainsが提供している静的コード解析サービスです。類似のサービスにはSonarQubeCodacyなどがあるかと思います。

公式サイトによると以下の特徴をもっています。

  • 2500以上のコードチェック
    • Qodana の広範なインスペクションポートフォリオを使用して、パフォーマンスの問題、潜在的なバグ、未使用の宣言、わかりにくいコードコンストラクト、命名規則とスタイル規則の違反などを検出します
  • サードパーティライセンス監査
    • アプリケーションで使用されているサードパーティのライブラリとフレームワークのライセンスがアプリケーションのライセンスに対応していない場合や商用プロジェクトで使用できない場合を検出します
  • 脆弱性チェッカー
    • プロジェクトにインポートされる脆弱性のある依存関係と API を検出し、セキュリティ違反を防止します
  • コードカバレッジレポート
    • テストによるコードカバレッジ状況を評価し、目標とするカバレッジレベルに必要なクオリティゲートを設定できます
  • ベースライン
    • 検出された問題をベースラインに追加して技術的負債を管理できます。チームが新たな問題を引き起こすのを防ぎ、技術的負債を管理しながら徐々に減らすことができます
  • クイックフィックス
    • 自動的にクイックフィックスが適用された状態でプルリクエストを作成するため、リクエストをレビューして、基準に一致するものだけを受け入れることができます
  • プルリクエストの自動レビュー
    • プルリクエストを自動的にレビューし、変更の影響を分析して、変更がコードベースにどのような影響を与えるかを示します
  • 明確な合格と不合格のクオリティゲート
    • 自動クオリティゲートは事前に定義されたベンチマークに対してコードを解析することで、即座にフィードバックを提供し、コードが特定の重大度の課題数を超過した場合にパイプラインを失敗させることができます
  • CI/CD インテグレーション
    • Jenkins、GitHub Actions、GitLab、TeamCity などの CI/CD ツールを統合できます
  • 一般的なプログラミング言語やフレームワークに対応している
    • Java, Kotlin, Android, JavaScript, TypeScript, PHP, C#, C, C++, Python, Go, HTML, CSS, Vue.js, VB.NET, Spring

Qodanaは静的コード解析の実行だけでなく静的コード解析の結果を蓄積と解析することができるQodana Cloudも提供されています。デモプロジェクトYouTubeに動画を公開されているのでよりイメージを掴むことができるかと思います。

https://www.youtube.com/watch?v=WrhnUnzMUCg

なぜ静的コード解析を導入することになったのか

一部のリポジトリにはSpotBugsなどが導入されていましたが標準化されておらず導入されていないリポジトリもありました。
そのため、静的コード解析の標準を決めることで全てのリポジトリに静的コード解析を導入を促し、導入したことでコードの品質を向上させコードをレビューする側(される側)の負荷を軽減させたい目的がありました。

静的コード解析を導入するために調査したこと

入社したばかりで静的コード解析の導入状況を把握できていなかったため全てのリポジトリに対して静的コード解析の導入状況を把握してから評価する静的コード解析ツールの選定を行いました。
選定した静的コード解析ツールたちに以下のことなどを調査しました。

  • 導入事例から注意しておきたいことなどがないか確認した

Qodanaは2023年にGAになったばかりのサービスだったためあまり導入事例はなかったのですが、SonarQubeなどは導入事例があったため注意しておきたいことなどを把握しました。

  • 動作確認用コードに対して機能の動作がドキュメントから想定した挙動と一致するか確認した

影響反映を小さくするために動作確認用に実装したコードに対して静的コード解析を導入して動作確認を実施しました。
これがほぼ最速の実行時間となるため、この後に実施する主要なリポジトリでの実行時間と比較に利用しました。

  • 主要なリポジトリに対して動作確認し指摘内容の調査や実行時間の計測した

Qodanaの動作を確認すると実行時間が想定よりも長かったこと、実行するのにメモリを多く消費することなどがわかったため回避方法を検討しました。

実行時間が想定よりも長かった問題は変更されたファイルのみを対象に静的コード解析を実行することで回避しました。
WealthNaviではGitHubとBitbucketを利用しているためGitHubではpr-modeを利用する、Bitbucketでは同様の機能がなかったためローカルのコミットされていない変更を検査するオプション(--script local-changes)で回避しました。

GitHub Actionsでpr-modeを有効にしたサンプル
  qodana:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up JDK 21
        uses: actions/setup-java@v4
        with:
          java-version: '21'
          distribution: 'corretto'
      - name: Qodana Scan
        uses: JetBrains/qodana-action@31d6f3309b31c566758e1314a3d9ef0dff75ecbd # v2024.2.6
        with:
          pr-mode: true # 初期値はtrueのため設定する必要は本来はない
        env:
          QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
Bitbucket Pipelinesでローカルのコミットされていない変更を検査するオプションを利用したサンプル
pipelines:
  branches:
    main:
      - step:
        name: Qodana
        image: jetbrains/qodana-jvm:2023.3
        script:
            - git fetch origin ${BITBUCKET_BRANCH}
            - git fetch origin ${BITBUCKET_PR_DESTINATION_BRANCH}
            - COMMIT_ID=$(git log origin/${BITBUCKET_PR_DESTINATION_BRANCH}..${BITBUCKET_BRANCH} --pretty=format:"%H" | tail -n 1)
            - git reset --soft ${COMMIT_ID}
            - export QODANA_TOKEN=$QODANA_TOKEN
            - qodana --project-dir=$BITBUCKET_CLONE_DIR --results-dir=$BITBUCKET_CLONE_DIR/.qodana --report-dir=$BITBUCKET_CLONE_DIR/.qodana/report --script local-changes

動作確認できていないのですが、2024.1バージョンで--diff-start/--diff-endオプションが追加されたためこちらの方がより今後はいいのかもしれません。

また、実行するのにメモリを多く消費する問題は--propertyオプションでメモリ上限を指定することで回避しました。

Bitbucket Pipelinesで--propertyオプションでメモリ上限を設定したサンプル
pipelines:
  branches:
    main:
      - step:
        name: Qodana
        image: jetbrains/qodana-jvm:2023.3
        script:
            - export QODANA_TOKEN=$QODANA_TOKEN
            # ヒープサイズをメモリの35%に設定
            - qodana --project-dir=$BITBUCKET_CLONE_DIR --results-dir=$BITBUCKET_CLONE_DIR/.qodana --report-dir=$BITBUCKET_CLONE_DIR/.qodana/report --property=-XX:MaxRAMPercentage=35

なぜ静的コード解析にQodanaを選定したか

Qodanaを選定したのはIntelliJ IDEAとの親和性が高かった、Qodanaの料金体系が開発者数ベースだったのが主な理由です。

  1. IntelliJ IDEAとの親和性が高かった

WealthNaviのバックエンドエンジニアはほとんどがJetBrainsが提供しているIntelliJ IDEAを利用しています。
QodanaはJetBrainsが提供しているサービスなので、プラグインを導入しないといけないなどの事前作業が不要ですぐにIntelliJ IDEAからQodanaを利用することができます。

これまでIntelliJ IDEAで指摘されていたコードの問題点もQodanaでも指摘してくれるため指摘内容が変わらないこと、 Qodana Cloud上に蓄積されているレポートをIntelliJ IDEAから確認することができるのも評価された理由です。

  1. Qodanaの料金体系が開発者数ベースだった

Qodanaは開発者数ベースの料金体系で、SonarQubeなどのサービスはコード数ベースの料金体系を採用しています。
コード数ベースだと新規プロダクトの開発が予定されている状況だったため、開発者数ベースの料金体系の方がコストを抑えされると判断しました。

Qodanaを普及するためにしたこと

Qodanaを導入する前からバックエンドエンジニア向けの開発標準を定義している開発ガイドラインが整備されていました。
既存の開発ガイドラインに以下などを記載した静的コード解析のガイドラインを追加しました。

  • 静的コード解析が必要な理由
  • 静的コード解析が担ってくれること
  • 静的コード解析の指摘に対する取り扱い
  • Qodana Cloudのアカウントやプロジェクトの運用方針
  • 標準で設定して欲しいQodanaの検出条件
  • CIパイプラインへのQodanaの組み込み方法

また、静的コード解析のガイドラインを認知してもらうために開発チームに導入説明会の実施とQodanaに関する問い合わせをするためのSlackのチャンネルを用意しました。

おわりに

明日は @3k11gr8 さんの Amazon Web Services (AWS) JDBC Driverに関する記事です。お楽しみに!

WealthNavi Engineering Blog

Discussion