駅メモ!チームでエンジニアをしている id:stakHash です。
弊社の主力プロダクトの 1 つである駅メモ!は、今年で 8 周年を迎えました 🎉
スマートフォンゲームとしては息の長いサービスですが、現在でも日々様々な新機能の開発が進んでいます。
今後も今以上の速度でユーザの皆様に価値提供をしていくためには、分かりやすく変更しやすいコードベースを維持・改善していくことが必要です。 しかし、「分かりやすさ」「複雑さ」という主観的でぼんやりとした感覚値は、長いライブサービスでは、人員の入れ替わりもあって判断が困難になっていました。
そこで、「複雑さ」 を定量的に計測する方法を探ってみました。
「複雑さ」とは
今回は、Microsoft 社が主に Visual Studio 内で利用している保守容易性指数 (Maintainability Index)を扱ってみます。
MAX( 0, (171 - 5.2 * ln( Halstead ボリューム ) - 0.23 * ( 循環的複雑度 ) - 16.2 * ln( コード行数 )) * 100 / 171 )
式の通り、これは 0~100 の範囲の値になります。低いほどそのコードが複雑で、保守しにくいことを表します。 Visual Studio では、次のように警告する範囲を決定しているようです。
保守容易性指数 | 警告色 |
---|---|
0~9 | 赤 |
10~19 | 黄 |
20~100 | 緑 |
20 が 1 つの基準値となるでしょうか。
さて、2 つの別のメトリクスが出てきましたので、簡単に説明します。
Halstead ボリューム
1 つ目は Halstead ボリューム (Halstead Volume) で、1977 年に Halstead 博士が導入した Halstead complexity measures という一連のメトリクスのうちの 1 つです。 詳しい説明はWikipediaに任せて、式を見ていきます。
Volume = (オペレータの数 + オペランドの数) * log2(オペレータの種類 + オペランドの種類)
式を見れば分かる通り、コード中の語彙の複雑さに注目していることが分かりますね。
Perl においては、 Perl::Metrics::Halsteadというパッケージが存在します (駅メモ!のサーバサイドは Perl で実装されています)。
循環的複雑度
2 つ目は 循環的複雑度 (Cyclomatic Complexity) です。
同じく詳しい説明はWikipedia先生にお任せしますが、 線形的に独立した経路の数 = 構造的な複雑さ を表します。
Perl においては Perl::Metrics::Simple で計測できます。
計測してみる
本題です。 今回計測したいのは、「複雑さ」という非常に抽象度の高い指標でした。
上記で紹介した「保守容易性指数」は「語彙的な複雑さ」と「構造的な複雑さ」の両面を考慮しており、「複雑さ」の計測に適した指標の 1 つであると考えられます。
これを計測する Perl モジュールが見つからなかったので、上述した 2 つのモジュールを参考に Perl::Metrics::Maintainability を実装しました。
このリポジトリ自体を計測してみると、全て 20 以上をマークしていました。 極端に複雑化していない事が確認できます。
MI LoC cc volume path -------------------------------------------------------------------------------- 39.67 48 14 1287.07 ./lib/Perl/Metrics/Maintainability/Result.pm 39.89 47 11 1460.16 ./bin/perlmi 39.95 49 14 1100.45 ./lib/Perl/Metrics/Maintainability/File/Result.pm 40.56 47 5 1526.19 ./lib/Perl/Metrics/Maintainability/File.pm 46.19 33 5 720.46 ./lib/Perl/Metrics/Maintainability.pm
では、駅メモ!の実装を計測してみると、全体の約 2% に当たるファイルが 10 を下回っていました。 改善のし甲斐がありそうですね! (ファイルパスは機密保持の観点から削除しています)
MI LoC cc volume path -------------------------------------------------------------------------------- 0.00 489 140 19917.78 0.00 437 208 23967.10 0.00 693 198 33429.84 0.00 216 199 15096.23 0.00 699 58 42585.91 0.00 1259 208 51969.83 0.00 653 116 31531.67 0.00 538 87 24446.56 0.00 866 109 43274.66 0.00 603 104 30685.96 0.00 417 156 22806.22 ...
まとめ
今回の計測により、今まで個々人が漠然と「ここは複雑そうだな」と思っていたものが、数値化・順位づけされて見えるようになりました。
実際にどこを改善していくかは、コードを精査する必要がありますが、コードベースの「複雑さ」を抑えていくための目安としては有効に使えそうです。