はじめに
先日FlutterNinjas2025に参加し、そこで紹介されたDCM(Dart Code Metrics)というDart&Flutter解析ツールが紹介されました。
最近コードの品質を保つのに少し悩んでいたこともあり、興味が湧いたので実際に使ってみました。
この記事では、DCMの導入から解析結果を軽く解説します。コードを綺麗に保ちたい、現在のコードを評価してみたい方は試していただければとおもいま
DCMは無料で使えるライセンスがあるため、今回は無料版を使ってみます。
導入
ライセンスキーの取得
こちらからStart for Freeを選択。
メールアドレスを入力しSubmit order
License Keyと手順が記載されたメールが届きます。
DCMのインストール
Homebrewのインストール
すでに開発を行っている方は入っていると思いますが一応
Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
DCMのインストール
Homebrewインストールに以下のコマンドを実施
brew tap CQLabs/dcm
brew install dcm
これで完了です。
エディタへの適用
今回はVS Codeに入れていきます。
いつも通りExtensionsにてDCMを検索しインストール。
チーム内で拡張機能を合わせる必要があれば.vscode/extensions.json
に以下を追記。
{
"recommendations": ["publisher.dcm"]
}
ライセンスをアクティベート
ライセンスはデバイスに紐づかれるため共有などはできません。
以下のコマンドでメールに届いたライセンスキーをアクティベートします。
dcm activate --license-key={ここにライセンスキーを入力}
以下のコマンドでDCM License: Free Version: x.xx.xと表示されたらOKです。
dcm license
有料版のみになりますがCI/CD環境でDCMを自動的に実行して品質をチェックできます。
プロジェクトに統合
DCMのルールを追加していきます。
有料版であれば recommended を設定するだけで、いい感じにルールを適用してくれるのですが、無料版ではルールを一つずつ追加していく必要があります。
とりあえず以下のページから無料のルールを詰め込みましょう。有料版だとあったら嬉しいルールがちらほら見受けられます。
https://dcm.dev/docs/rules/
今回は無料以下のFlutterのルールを追加してみます。以下をanalysis_options.yaml
に追記します。
dart_code_metrics:
rules:
- always-remove-listener
- avoid-expanded-as-spacer
- avoid-returning-widgets
- avoid-shrink-wrap-in-lists
- avoid-wrapping-in-padding
- check-for-equals-in-render-object-setters
- prefer-const-border-radius
- prefer-correct-edge-insets-constructor
- prefer-text-rich
ルール名 | 説明 |
---|---|
always-remove-listener | イベントリスナーを追加したが、削除していない場合に警告します。 |
avoid-expanded-as-spacer | 空の SizedBox/Container ウィジェットを含む Expanded ウィジェットの代わりに Spacer ウィジェットを使用することを提案します。 |
avoid-returning-widgets | メソッドや関数が Widget または Widget のサブクラスを返す場合に警告します。 |
avoid-shrink-wrap-in-lists | shrinkWrap パラメータを持つ ListView ウィジェットが Column、Row、または別の ListView ウィジェットにラップされている場合に警告します。 |
avoid-wrapping-in-padding | ウィジェットが Padding ウィジェットにラップされているが、自身に padding 設定がある場合に警告します。 |
check-for-equals-in-render-object-setters | RenderObject のセッターで、新しい値に対する等価性チェックがない場合に警告します。 |
prefer-const-border-radius | BorderRadius.circular コンストラクタの代わりに const BorderRadius.all コンストラクタを使用することを提案します。 |
prefer-correct-edge-insets-constructor | 正しい EdgeInsets コンストラクタを使用することを提案します。 |
prefer-text-rich | RichText ウィジェットの代わりに Text.rich を使用する場合に警告します。 |
ルールが多すぎてよく分からないという方は、まず以下のコマンドを使って、dcmで利用可能なすべてのLintルールで解析してみましょう。
どのルールに引っかかっているのかを確認したうえで、必要なものだけを追加するかどうかを判断できます。
dcm init preview lib
ルールを設定したら解析をしていきましょう。
コード解析
コマンドは基本的にdcm [解析種類] <解析したい場所>
になります。
今回はlibフォルダに対して様々な解析を行っていきます。
まずは以下のコマンドでLintルールに基づいて分析し、問題を見つけてくれます。
dcm analyze lib --reporter=console
たくさんの issues が出てきました。
--reporter=console
オプションをつけることで、どのファイルでどんな警告が出ているのかを確認できます。
プロジェクトのアーキテクチャを可視化する
ファイルの依存関係を可視化することができます。
dotファイルを生成しそれを可視化するためのGraphvizが必要であるため以下でインストールします。
※インストールに少し時間がかかります。
brew install graphviz
以下のコマンドでファイルの依存関係を可視化することができます。プロジェクトのルート直下にpng画像が生成されます。
dcm analyze-structure lib | dot -Tpng -Gdpi=200 -o example.png
実際の画像はお見せできませんが、以下のような図が出てきます。
今回解析したプロジェクトではかなりグチャグチャな図がでてきました。アーキテクチャを見直す必要があるようです。
コードメトリクス解析
コードのメトリクス(品質、複雑さ、保守性など)を算出してくれます。
まずはanalysis_options.yaml
に以下からメトリクスの設定を選んで追加します。
https://dcm.dev/docs/metrics/
今回は以下で設定します。設定値に関しては様々な資料があるため調べて設定してみましょう。数値についてはデフォルトのものを使用しています。
dart_code_metrics:
metrics:
# 循環的複雑度(分岐やループを調べて複雑さを測る)
cyclomatic-complexity: 20
# コード行数
lines-of-code: 80
# Halsteadボリューム(コードのサイズや記述労力を推定する)
halstead-volume: 150
# 保守性指数 (循環的複雑度や行数、Hslsteadボリュームから算出)
maintainability-index: 50
# 最大ネストレベル
maximum-nesting-level: 5
# パラメータ数
number-of-parameters: 4
# Widget数
number-of-used-widgets: 20
設定できたら以下のコマンドで解析しましょう。
dcm calculate-metrics lib
結果がでました。
それぞれどのような結果なのか見てみましょう。
今回は循環的複雑度(CYCLO)、Halsteadボリューム(HALVOL)、保守性指数(MI)の3つのメトリクスに注目してみます。
CYCLO(循環的複雑度)
コード内の分岐やループの数を数値化したもので、コードの複雑さを示す指標です。この数値が高いほど、コードの理解やテストが難しくなります。
結果
最小値:0
最大値:21
平均値:2.3
これは、分岐やループのない非常に単純なメソッドもあれば、最大で21もの分岐やループを含む複雑なメソッドも存在することが分かりました。
平均値は2.3で、複雑なメソッドが少ない良い数値と言えるでしょう。
HALVOL(Halsteadボリューム)
Halstead複雑度のボリュームはコード上にある演算子(+
,-
,&
など)とオペランド(変数名や数字)の種類と出現数をもとに、コードの大きさや複雑さを測ります。
数値が高いほどコードが大きく複雑であるとされ、一般的に100~1000が標準的な範囲、1000~8000以上はかなり高いと見なされるようです。
結果
最小値:0
最大値:3687
平均値:148
最大値3687はやや高めな数値で、膨大で複雑な処理を行っているメソッドがあることを示しています。
平均値は148と比較的低いので全体としては問題なさそうですね。
MI(保守性指数)
循環的複雑度や行数、Hslsteadボリュームから算出される値で、保守性(理解しやすいか、修正や機能追加が容易か)を測ります。
デフォルト数値が50で、この数値が低いほど改善が必要になります。以下の評価基準を見ると20を切ったら気をつけたほうがいいようです。
結果
最小値:19
最大値:100
平均値:75
最小は19で20を下回っているため対象のコードは保守性の改善が必要そうですね。平均値は75で全体としては問題なさそうです。
無料版ではできない分析
widgetやassetsの解析、コード品質のチェックもできますが、無料版ではissueの数だけ表示されどのコードが問題かは表示されません。
また、dcm analyze や dcm check-unused-code で見つかった問題は dcm fix で自動修正できますが、これも有料版でのみ利用可能のようです。
社内で有料版の稟議を出して時間があるときに解析してみたいと思います。
使ってみての感想
コーディングスタイルの統一や潜在的なバグを発見するLinterとは別に、DCMがコードの循環的複雑度や保守性指数といった数値でコードの健全性を可視化できるのはかなり面白いし、リファクタリングが捗りそうだと感じました。
ただ、無料版ではDCMの強みであるWidgetの解析や詳細な品質チェック機能が制限されているため、大規模なプロジェクトで利用するには有料版の導入が必要そうですね。
参考文献
https://jp.mathworks.com/discovery/cyclomatic-complexity.html
https://learn.microsoft.com/ja-jp/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2019