そのリファクタリング、今やるべき? Tidy First?から学ぶ「やらない判断軸」と複雑さの可視化

TL;DR

AI Agentに「Cyclomatic Complexity, Cognitive Complexityを計測してマークダウンレポートを作成して」と指示を出せばOK。

はじめに

※この記事は社内向けに書いたものを一部リライトしています。

「このモジュール、なんとなく読みづらい」「修正するたびにバグが出る」「ちょっとした変更をするために手間が多すぎる」といったコードの品質を、感覚ではなく数値で語れるようにしたくないですか?

CARTAには技術力評価会という制度があり、そこでコード品質について言及することもあります。リファクタリングは必要だと認識しつつも、スコープ・アプローチ・タイミングについて議論になったりします。

僕自身もリファクタリングのタスクを進めていく中で、どのようにタスクの優先度を捉えるべきか考える機会があり、そこから学んだことを紹介します。

なんのための計測?

コードの品質を計測する目的は、単に高い品質を確認してニヤニヤするだけではありません。改善が必要な低い品質のコードを見つけ出すことにあります。

『Tidy First?』で語られている通り、闇雲にリファクタリングするのではなく、まずは「整頓しない」ことを検討するべきです。いくら品質の低いコードであろうと、変更しないコードを整頓するべきではありません。

(ちなみに、CARTA HOLDINGSではO'Reilly Online Learningを導入しており、エンジニアであれば無料で『Tidy first?』をはじめとする多くの書籍を読むことができます)

では、「整頓する」判断はどのようにするべきか? 変更があったとしても、“きれいな”コードであれば整頓の必要はないでしょう。多少汚くても許容できるかもしれません。
「整頓する」なら、それによる見返りがどれほどあるか(どれだけコードが整頓されるか)把握しておく必要があります。

ビジネス的観点から見るなら、整頓の価値は「不具合の抑制」「開発スピードの向上」です。
複雑なコードはバグを生み、開発を遅らせ、それは積み重なり負の連鎖を招きます。適切なタイミングで解消すること(いわゆる「技術的負債の返済」)が、ビジネスの継続には不可欠です。

ということで、今回の「複雑さの可視化(定量化)」という話になるわけです。
ただし、定量化はひとつの手段に過ぎません。この手法にこだわる必要はないですし、もっと感覚的なアプローチを採ってもいいと思います。定量化は手段が目的化しやすいので注意して導入しましょう。

やってみよう

少し調べてみると、コード品質の可視化でいえばSonarがデファクトスタンダードのようです。技術者向けの記事やブログなど、コードのメンテナンス性についていろんな情報を発信しているので、タスクの方針を考えるにあたって大いに参考になりました。

本格的にチームや組織で継続的な計測を行う場合は、CI/CDに組み込めるSonarCloudSonarQubeの導入を検討するのが間違いないかもしれません。ただ、今回は継続的な改善をスコープ外と考え、セットアップの手間が少ないローカルで完結する手法を採用しました。

ここまでいろいろと書きましたが、AI Agentがある昨今、やることは以下のような指示を出すだけです。

# 指示
src/domain/pricing/PriceCalculatorモジュールのコードの複雑度を計測してください。

# 要件
1. eslint-plugin-sonarjs と eslint-formatter-summary を使用する前提で進めてください。
2. 各モジュールとモジュール全体の複雑度 (Cyclomatic Complexity, Cognitive Complexity)を定量化してください。
3. 必要なパッケージのインストールが必要な場合は、コマンドを提示(または実行)してください。
4. 作業後、作業内容(使ったコマンド、出力結果)をまとめてマークダウンのレポートを作成してください。

今回計測するモジュールはTypeScriptなので、ESLintとSonarJSプラグインを組み合わせて利用します。他の言語でも似たようなツールがあると思いますし、一時的な計測であればAIにスクリプトを書かせてもいいかもしれません。このあたりはAIに調べさせたり壁打ちするといいですね。

そして、今回は2つの指標を計測しています。

1. Cyclomatic Complexity (循環的複雑度): コードの分岐の多さ。テストケースの数に直結します。
2. Cognitive Complexity (認知的複雑度): ネストの深さなど、人間がコードを理解する難しさを表しています。

VSCodeのGitHub Copilot Agent (Claude Opus 4.5)が作成したレポートから一部を抜粋します。

7. calculateTotalPrice.ts

行番号 関数 Cyclomatic Complexity Cognitive Complexity
20:19 calculateTotalPrice 7 6
22:44 内部forEach callback 6 3
89:50 getSeasonalDiscountRate 14 2
99:96 isValidCoupon 2 -

calculateTotalPrice.ts モジュールの循環的複雑度のスコアが 14 で「注意」となっていますが、該当部分の認知的複雑度のスコアは 2 で無視できます。
これは分岐の多い平坦な switch-case で発生しているもので、循環的複雑度だけを見ていると高くなってしまう部分ですね。2つの指標を見ることで簡単に判断できるようになっています。

今回のケースでは、全体的にコードの複雑度は低いという評価になりました。
結果として「今すぐの整頓価値は低く、将来的なタスク着手前で良い」という判断を下しましたが、「なんとなく」ではなく数値という根拠を持って「整頓しない」と決められたのはよかったと思っています。
プロダクトのフェーズやビジネスの状況によって、数字の読み解き方や「整頓」の優先度は変わります。 時にはアクセルを踏むために、時には守りを固めるために。定量化をうまく使って、ビジネスの成長に貢献するエンジニアリングをしていきたいですね。

おまけ

昨年 GitHub から GitHub Code Quality (Code Scanning) がリリースされました。

Code Quality はリポジトリで設定しているポリシーへの違反や脆弱なコードを検知・自動修正するための仕組みで、今回のような複雑度を計測する仕組みではありません(検知するための基準として、内部的に計測している場合はあります)。

複雑度の計測は「どう直すか(解決方法)」の考案を別にしないといけないので、以下のような使い分けをするとよさそうです。

  • 複雑度計測: 設計レベルでの見直しやリファクタリング計画に使う
  • Code Quality: セキュリティリスクや明らかなバグの芽を自動で潰す

泥沼でどう改善したらいいのかわからないと考えているなら、まずは Code Quality を導入して「明らかな悪いコード」を機械的に排除するのも手かもしれません。TypeScript以外にも様々な言語に対応していてセットアップも非常に簡単なので、すぐ試せるのがいいですね。