リファクタリングによる技術的負債の返済をNDependで可視化してみた

この記事では、NDependを使ってどのように技術的負債を返済できるか検証した内容を紹介します。

# イントロダクション

NDependの作者であるPatrick Smacchia氏からライセンスをいただきました。しかし、最近の私の役割はインフラ寄りでアプリケーション開発から離れているため、業務で使う機会がありませんでした。そこで、過去に習作したソースコードをリファクタリングしてみることにしました。

こちらが今回リファクタリングしてみたソースコードで、簡単なデスクトップキャプチャツールです。

https://github.com/JHashimoto0518/tiny-desktop-capture

15年前に書いたコードなので稚拙そのものですが、リファクタリングの実践にはちょうどいいと考えました。

Patrick氏に尋ねたところ「古いプロジェクトをベースラインとして設定し、リファクタリング後のプロジェクトと比較して、どの程度改善されたかを可視化する」機能がマッチするとアドバイスされました。今回はこの機能を試して、リファクタリングによる改善をどのように可視化できるかを示します。

# NDependとは

NDependは、.NETコードを分析するための静的解析ツールです。主な機能には、コード品質・アーキテクチャの視覚化、コードの変更のトラッキングなどがあります。

Patrick氏がNDependについて語っている記事です。

https://www.infoq.com/jp/articles/patrick-smacchia-interview/

# ソースコードの分析

VisualNDepend.exeを起動してVSプロジェクトを解析します。VSプロジェクトとの紐付けはNDependのプロジェクトとして保存できます。

解析結果はダッシュボードで可視化されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9964/369eb13d-b054-bdd5-6d9d-885d787e6fa3.png)

# ベースラインの設定

リファクタリングの前にベースラインを設定します。

まず、元のソースコード`v1.0`をコピーし`v2.0`を作成します。次に、v1.0/v2.0両方のNDpendプロジェクトを作成し、v2.0のベースラインとしてv1.0を設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9964/ab93d9ef-2060-4b1c-3fb5-600c67bff80c.png)

以降は、v1.0は修正せずv2.0をリファクタリングします。

# ホットスポットを探索する

NDependには、リファクタリングすべき箇所の見極めを支援する強力な機能があります。

先ほどのダッシュボードの`Debt`（技術的負債）に着目します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9964/bc07d0d1-53d0-60b0-7351-9fb3963a35be.png)

`Types to Fix Priority`を選択すると、ブレークポイントの低い`issue`を含むクラスがリストされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9964/2967d31f-e109-9436-afde-780d37f9d474.png)

ブレークポイントの低さはコード品質の向上において投資価値の高さを表しており、優先度を判断する指標になります。ブレークポイントの考え方については公式ドキュメントをご参照ください。

https://www.ndepend.com/docs/find-hot-spots#prioritize-issues-fix

今回はブレークポイントが最も低く、かつレートが`E`の`MainForm+Config`を修正することにします。

なお、レートはSQALEに基づいています。

https://www.ndepend.com/docs/technical-debt#DebtRating

# ホットスポットをリファクタリングする

`MainForm+Config`の`Issues`をクリックすると、Issueがリストされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9964/1105bd12-7170-5fce-34df-c387ab0253a2.png)

フィールドに起因するIssueが多く検出されているので、プロパティに変更します。

```csharp:v1.0
/// <summary>
/// 設定
/// </summary>
private class Config {

    /// <summary>
    /// 画像の形式
    /// </summary>
    public string ImageType;

    /// <summary>
    /// 画像の倍率
    /// </summary>
    public decimal Magnification;
}
```

```csharp:v2.0
/// <summary>
/// 設定
/// </summary>
private class Config {

    /// <summary>
    /// 画像の形式
    /// </summary>
    public string ImageType { get; set; }

    /// <summary>
    /// 画像の倍率
    /// </summary>
    public decimal Magnification { get; set; }
}
```

ビルド後にもう一度分析すると、ベースラインからどのくらいの負債を返済できたかがわかります。また`Method Complexity`など他のメトリクスの改善も可視化されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9964/ab441cc6-3736-66bc-7099-905d38cd9caa.png)

# 新たに負債を作りこんでいないか？

リファクタリングにより、新たに負債を生み出していないことが重要です。

https://www.ndepend.com/docs/write-clean-code

先ほどのダッシュボードを再掲します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9964/bf27de55-a85d-3345-ca87-fbe7b1df747c.png)

これは、ベースラインから新たに４つのIssueが発生していることを示しています。これらは、先ほどのリファクタリングで新たに作りこんでしまった負債です。

`+4`をクリックすると、Issueが発生しているクラスがリストされます[^1]。

[^1]:メニューの`Issues` > `New Debt and Issues per Rule`でもベースラインから新たに追加されたIssueをリストできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9964/b20c89a6-8c98-8c80-6a1e-a206867862f8.png)

さらに該当クラスの`typeIssues`をクリックすると、issueを確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9964/208e71cf-0282-8d58-d5e6-7325b2de95c8.png)

いずれも、`MainForm+Config`クラスがパブリックでないにもかかわらず、プロパティの可視性がパブリックになっていることを指摘されているので、`internal`に修正します。

```csharp:v2.0
private class Config {

    /// <summary>
    /// 画像の形式
    /// </summary>
    internal string ImageType { get; set; }

    /// <summary>
    /// 画像の倍率
    /// </summary>
    internal decimal Magnification { get; set; }
}
```

ビルド後にもう一度分析を実行し、新たなIssueが解決されたことを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9964/2ef256c4-c4b1-4258-cc4f-b567846401b8.png)

# ベースラインとの比較を可視化する

先述の通り、ベースラインからどのくらいの負債を返済できたか比較できます。

また、他にも様々な視点で比較できます。

例えば、メニューから`Diff` > `Code Diff Summary` > `Types where code was changed`を選択すると、先ほどのリファクタリングで変更されたクラスがリストされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9964/2bcbbe65-b840-a6cd-7d92-13dd3c9d87d9.png)

さらに、コンテキストメニューの`Diff Source`を選択すると、該当クラスのベースラインとの差分が表示できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9964/8506c550-bf17-8991-0e62-6fe38315c2b6.png)

# まとめ

今回は、ベースラインとの比較を中心にNDependをどのようにコード品質の改善に活用できるか解説しました。

検証してみて、NDpendには次の効果が期待できると感じました。

- 負債による損失やリファクタリングによる返済の定量化によるビジネスサイドへの訴求効果。すなわちリファクタリングを提案する際の根拠として利用。
- コード品質向上の達成率や、目標値に対しての現在のコード品質の可視化によるデベロッパーのモチベーション向上

また、NDpendには他にも多くの機能があり、ユースケースとして紹介されています。いずれ他の機能も試してみたいと思います。

https://www.ndepend.com/docs/ndepend-use-cases