Lattix Python APIで循環依存のリファクタリング優先順位をつけてみた

Lattix

この記事の要約

本記事で紹介するスクリプト(export_cycles_high_level_report.py)を実行すると、サブシステム間の循環依存を一括検出したHTMLレポートが出力されます。優先順位の付け方は以下のとおりです。

  1. Depthが浅く、sizeが大きい循環から優先的に確認する(浅い階層で多数のサブシステムが絡む循環ほどアーキテクチャ全体への影響が大きい)
  2. その中で逆依存に注目し、Index差が大きいものから着手する(離れた要素間の逆依存ほど重要)

ExcelレポートやPlantUML / Mermaidのグラフ出力にも対応しており、コマンドラインオプションでフィルター条件を指定した一括出力も可能です。

はじめに

最近お客様とのお打ち合わせなどで、こんな話を聴きました。

「Lattixで循環依存が見つかるのはわかった。でも大量に出てくると、どこから手をつければいいかわからない」

循環依存の検出自体はLattixのGUIで簡単にできますが、限られた工数の中でどの循環を優先的に解消すべきかを判断するには、影響度や重要度を定量的に比較する必要があります。

今回は、Lattix Python APIを活用してサブシステムレベルの循環依存を一括検出・可視化し、リファクタリングの優先順位付けを支援するスクリプトを作成・実行してみました。

この記事でやること

  • Lattix Python APIでサブシステム間の循環依存を再帰的に検出する
  • 検出された循環依存構造をPlantUML / Mermaid / HTMLで可視化する
  • 「逆依存(本来の依存方向に逆らう依存)」と「Index差(DSM上での距離)」を指標として優先順位を判断する

背景:なぜ優先順位付けが必要なのか

LattixのGUIで[レポート]-[循環レポート]-[循環]から出力されたレポートを確認すると、プロジェクトに含まれるサブシステム間の循環グループとそれを構成するサブシステムが一覧表示されます。
しかし実際のプロジェクトでは、循環依存が数十件検出されることも珍しくありません。
また、従来の[循環]レポートでは「どの依存関係が循環の原因になっているのか」までは示されないため、具体的な着手箇所の特定が難しいという課題もあります。

すべてを一度に解消するのは現実的ではないため、以下のような観点で優先順位をつける必要があります。

観点内容
システム階層(Depth)レイヤーや大きなサブシステム(Depth=小さい)をまたぐ循環依存ほどアーキテクチャへの影響が大きい
逆依存の有無逆依存は上位レイヤー(appなど)の変更を下位レイヤー(ライブラリなど)へ伝えてしまう原因となり、他の上位レイヤーにまで変更の影響が伝搬するリスクがある(影響範囲が広い場合はテストで見つけきれない)。このため浅い階層のレイヤー間・サブシステム間をまたぐ逆依存ほど影響が大きく、優先的に解消すべき。逆に深い階層の逆依存は小さなモジュール内部の問題であり、許容できる場合も多い
Index差DSM上でのサブシステム間のIndex差が大きいほど、アーキテクチャ上の離れた要素間での違反である。例:app→middle→driver→utility→libというレイヤー構成で、libからappへの逆依存はIndex差4。libに依存しているレイヤーは多いため、この逆依存はapp変更時にapp→lib→その他のレイヤーという経路でシステム全体へ影響を伝搬させるリスクを生む
依存の種類(Kind)依存の種類によって問題の重要度が異なる。例:関数呼び出しやグローバル変数のRead/Writeは問題を生じさせやすい依存関係であることが多い

今回のスクリプトは、これらの指標を自動的に算出し、循環の原因となっている個々の依存関係をファイル・関数レベルで特定できるレポートとして出力します。

前提・準備

本記事のスクリプトはLattix Python APIを使用するため、Lattixのコマンドラインライセンスが必要です。お持ちのライセンスの種類や評価ライセンスのご希望については、Lattix 製品カスタマーセンター までお問い合わせください。

本記事の検証環境

項目
OSWindows 11 Pro
Python3.12.2
Lattix2025.1.7
テスト用プロジェクトApache httpd 2.0.55(C/C++ Understandモジュール)

ダウンロード

本記事で使用したスクリプト・設定ファイル・出力レポートをまとめたzipファイルをダウンロードできます。

export_cycles_high_level_report.zip

環境構築やスクリプトの実行手順については、zip内の README.md を参照してください。

注意: 本記事執筆時点では、検証はC++(Understand)のみで実施しています。C++(Clang)、Java、.NET用のプリセットファイルは参考として同梱していますが、これらの言語での動作検証は行っておりませんのでご了承ください。

サンプルレポート

zip内の出力レポート(work/httpd-2_cycles_high_level/)の index.html はそのままブラウザーで開いて動作を確認できます。実際のレポートの見た目や操作感を事前に把握したい場合にご活用ください。

利用手順1: 出力結果を確認する

出力ディレクトリの構成

httpd-2_cycles_high_level/
├── index.html              # HTMLインデックス(メイン閲覧用)
├── depth1_cycle_1.puml     # PlantUML(循環ごと)
├── depth1_cycle_1.md       # Mermaid Markdown(循環ごと)
├── depth2_cycle_2.puml
├── depth2_cycle_2.md
├── ...

ファイル名は depth{深さ}_cycle_{通し番号} の形式です。Depth順に並ぶため、エクスプローラーでソートすると浅い階層(=影響大)の循環から確認できます。

HTMLインデックスで全体を俯瞰する

index.html を外部ブラウザー(Chrome, Edge等)で開くと、全循環コンポーネントの一覧が表示されます(※グラフ描画・Excel保存機能のため、インターネット接続が必要です)。

注意: VS Code内蔵ブラウザーでは vscode:// リンクが動作しません。必ずChromeやEdgeなどの外部ブラウザーで開いてください。

一覧では、各循環が以下の形式で表示されます(例:zip同梱サンプルより)。

[Depth 1] Cycle 1 (size=4) in src.httpd-2_0_55
[Depth 2] Cycle 14 (size=2) in httpd-2_0_55.server
[Depth 2] Cycle 21 (size=3) in httpd-2_0_55.modules

表示項目意味
Depth循環が存在する階層の深さ(小さいほどアーキテクチャへの影響大)
size循環に参加しているサブシステム数(大きいほど複雑)
in …循環が含まれる親サブシステム名

まず注目すべきは Depthが小さく、sizeが大きい循環 です。浅い階層で多数のサブシステムが絡む循環は、アーキテクチャ全体への影響が大きいためです。

利用手順2: レポートの詳細画面を確認する

一覧から循環をクリックすると、詳細画面に遷移します。詳細画面は「グラフ表示」と「依存関係テーブル」の2つのセクションで構成されています。

グラフ表示

※上図は逆方向の依存関係のみ表示した図

下図は Cycle 1(depth=1, size=4)の出力例です。(全依存関係を表示しています。)

  • 黒色のエッジ: 順依存(DSMのIndex順に沿った正常な依存方向)
  • 赤色のエッジ: 逆依存(DSMのIndex順に逆らう依存=アーキテクチャ違反候補)
  • エッジのラベル: [Kind(数字)] の形式で、依存関係の種類と該当する依存の件数を表示

主なKindの意味は以下の通りです。

Kind意味
Includeヘッダーファイルのインクルード(#include
Invoke関数・メソッドの呼び出し
Globalグローバル変数の参照(Read)
Modify Globalグローバル変数への書き込み(Write)
Use Macroマクロの使用
in Macro Definitionマクロ定義内での参照
Reference型・変数・定数などへの参照
Weak Type型の間接的な参照(ポインター型など)
Function Pointer Reference関数ポインターの参照
Declaration宣言の参照

上記グラフの例では、server→modules、include→server、srclib→server/include/modules への依存が逆依存として検出されています。

依存関係詳細テーブル

グラフの下には、エッジごとの詳細情報がテーブルで表示されます。

※なお、「ソース(Source)」とは依存元、「ターゲット(Target)」とは依存先を意味します。

内容
方向順依存 / 逆依存
Depth循環が存在する階層の深さ(小さいほどアーキテクチャへの影響大)
Index差ソースとターゲットのDSM上での距離
Source Subsystemソースサブシステム名
Source Fileソースファイルパス(リンク付き)
Source Memberソース関数・メンバー名
Kind依存種類(Include, Data等)
Line行番号(リンク付き)
Target Subsystemターゲットサブシステム名
Target Fileターゲットファイルパス(リンク付き)
Target Memberターゲット関数・メンバー名

Source FileやLine列のリンクをクリックすると、VS Codeで該当ソースコードが直接開きます(vscode://file/ プロトコル)。リファクタリング対象の特定がスムーズに行えます。

注意: ソースコードへのリンク(vscode://file/)は、レポートを出力したローカルマシン上でのみ機能します。zip同梱のサンプルレポートや、他のマシンで出力されたレポートではファイルパスが一致しないため、リンクからVS Codeでソースコードを開くことはできません。

利用手順3: フィルターで優先順位を絞り込む

詳細画面上部のフィルターセクションを使って、表示内容を絞り込みます。

方向フィルター:逆依存に集中する

「逆依存のみ」ボタンをクリックすると、グラフ・テーブルの両方が逆依存のエッジだけに絞り込まれます。以下は Cycle 1 に「逆依存のみ」フィルターを適用した状態です。

順依存のエッジが非表示になり、逆依存(赤色)のエッジだけが残っています。テーブルも自動的にIndex差の大きい順にソートされるため、最も重要な逆依存が上位に表示されます。

逆依存の意味:

  • 順依存: Componentアルゴリズムで最適化されたIndex順に沿った依存(正常)
  • 逆依存: Index順に逆らう依存(本来あるべきでない方向の依存)

Index差で重要度を判断する

「Index差」はソースとターゲットのDSM上での距離を表します。レポート上において逆依存フィルターを適用すると自動的にIndex差の大きい順にソートされるため、最も重要な逆依存がテーブルの上位に表示されます。

Kindフィルター:依存の種類で絞り込む

Kindフィルターでは、依存関係の種類ごとに表示のON/OFFを切り替えられます。

  • Include: ヘッダーファイルのインクルード関係。コンパイル順序に直結するため影響が大きい
  • Invoke: 関数呼び出し。実行時の依存関係
  • Use Macro / in Macro Definition: マクロの使用・定義。プリプロセッサレベルの依存

例えば「Include」だけをONにすれば、コンパイル時の循環依存に絞って確認できます。

プリセット:フィルター設定を保存・再利用する

よく使うフィルター設定はプリセットとして保存できます。

  1. フィルターを好みの状態に設定する
  2. プリセット名を入力して「保存」をクリック
  3. 次回以降はドロップダウンから選択して「適用」で復元

config.jsonのpresets_fileに外部プリセットファイルを指定しておけば、チーム内で共通のフィルター設定を共有することもできます。複数ファイルを指定でき、すべてのプリセットがHTMLレポートのドロップダウンに表示されます。プリセット定義ファイルの書き方や提供済みプリセットの一覧については、zip内の README.md を参照してください。

下図はC/C++(Understand)モジュール用のプリセットを表示した状態です。

画像・Excelのダウンロード

レポート画面から直接ダウンロードも可能です。

ボタン機能
📥 画像を保存現在表示中のグラフをPNG画像でダウンロード
📥 PlantUML現在表示中のグラフをPlantUML(.puml)形式で保存
📥 Mermaid現在表示中のグラフをMermaid Markdown(.md)形式で保存
📥 Excelで保存フィルター適用済みのテーブルをExcelファイルでダウンロード

Excelダウンロード時は「グラフを添付」チェックをONにすると、グラフ画像もExcelに埋め込まれます。ファイルパスのハイパーリンクも含まれるため、Excelからソースコードへ直接ジャンプできます。

発展:コマンドラインでExcel一括出力する

チームでリファクタリング計画を立てる場合や、定期的にレポートを取得したい場合は、コマンドラインからExcelを一括出力することもできます。

# 全循環をExcel出力
python ..\scripts\export_cycles_high_level_report.py --excel

# Include逆依存のみにフィルタしてExcel出力
python ..\scripts\export_cycles_high_level_report.py --excel --preset "Include_Reverse" --depth shallowest+1

--preset オプションでプリセットを指定すると、そのフィルター条件でExcelが生成されます。--depth と組み合わせれば、「浅い階層のInclude逆依存だけ」といった出力も可能です。CI/CDパイプラインに組み込めば、定期的な循環依存の監視にも活用できます。

まとめ

今回は、Lattix Python APIを使って循環依存のリファクタリング優先順位付けを自動化するスクリプトを試してみました。

実現できたこと

  • 全階層の循環依存を一括検出: GUIと同等の検出結果を、コマンドラインから一括で取得
  • 可視化による直感的な把握: PlantUML / Mermaid / HTMLで循環構造を図示
  • 優先順位付けの自動化: Depth・逆依存・Index差・Kind の4つの観点で重要度を判定
  • チーム共有: Excel出力でレビュー会議やリファクタリング計画に活用

リファクタリング優先順位付けの流れ

  1. Depthが浅くsizeが大きい循環から確認する
  2. 「逆依存のみ」フィルターでアーキテクチャ違反を特定する
  3. Index差の大きいものから着手する
  4. Kind(Include等)で実害の大きい依存に絞り込む
  5. Excel出力してチームで共有・計画を立てる

循環依存の解消は一朝一夕にはいきませんが、「どこから手をつけるか」を客観的に判断できる仕組みがあると、チームでの合意形成もスムーズになります。Lattix Python APIを活用して、効率的なリファクタリング計画を立ててみてはいかがでしょうか。

おわりに

今回は循環依存のリファクタリング優先順位を決めるのに役立つスクリプトについてご紹介いたしました。
その他、ご不明な点やご質問などありましたら、Lattix 製品カスタマーセンター までお気軽にお問合せください。