【テクニカル・上級編】依存関係の「複雑度」をスコアリング:リンクが多すぎるタスクを自動抽出する品質管理 – Project VBA解析バイブル

スポンサーリンク

依存関係の「複雑度」をスコアリング:リンクが多すぎるタスクを自動抽出する品質管理

レガシーなプロジェクト管理の現場において、Microsoft Project(MSP)のVBA自動化は、単なる定型作業の効率化手段ではない。それは、腐敗しかけたWBS(Work Breakdown Structure)の構造的欠陥を暴き、破綻寸前のスケジュールを救済するための外科手術である。

現場で最も恐ろしいのは、ガントチャートの見た目がどれほど美しく整っていようとも、その裏で「蜘蛛の巣のように複雑怪奇に絡み合った依存関係(Predecessors / Successors)」が形成されている状態だ。1つのタスクの遅延がドミノ倒しのように全社へ波及し、誰も全体像を把握できない「ブラックボックス」と化したWBSは、プロジェクトを確実に死に至らしめる。

今回は、Project VBAのオブジェクトモデルの深淵を抉り、タスクが持つ依存関係の「複雑度(Complexity)」を数学的にスコアリングし、メンテナンス不能なボトルネックタスクを自動抽出する品質管理エンジンを解説する。

1. なぜ依存関係の「数」だけでは品質管理できないのか

タスクの先行・後続リンク(`Task.Predecessors` / `Task.Successors`)を単純にカウントするだけでは、真の複雑度は測れない。ここに、プロジェクト管理における構造的罠がある。

シニアエンジニアが考慮すべき複雑度のファクターは以下の3点に集約される。

1. 次数(Degree Centrality)の爆発: 1つのタスクに集中する先行・後続の総数。
2. 依存関係のトポロジー(型): 単なる終了-開始(FS)だけでなく、開始-開始(SS)やラグタイム(Lag)が混在している場合、スケジュールの変動係数は跳ね上がる。
3. クリティカルパス上の重み: 余裕時間(Slack)がないクリティカルタスクにリンクが集中している場合、その影響度(Blast Radius)は計り知れない。

これらを統合した独自の「依存関係複雑度スコア(Dependency Complexity Score: DCS)」を算出するアルゴリズムをVBAで実装する。

2. アーキテクチャ設計とメモリ最適化の極意

Project VBAで数千件規模のタスクを走査する場合、COMオブジェクトの参照リークと暗黙のオーバーヘッドがパフォーマンスを殺す。特に`Task.Predecessors`等のコレクションをループする際、不適切な変数宣言はメモリフラグメンテーションを引き起こす。

以下の設計原則を遵守する:

  • 厳密なオブジェクト解放: 取得したコレクションやタスクオブジェクトは、ループの都度 `Nothing` を代入して即座にRCW(Runtime Callable Wrapper)の参照カウンタを解放する。
  • 画面描画の完全停止: `Application.ScreenUpdating = False` により、VBA実行中のUIスレッドのブロックを回避し、処理速度を最大10倍以上に引き上げる。
  • エラーハンドリングの堅牢性: 削除済みタスクや循環参照による例外を完全にトラップする。

3. 実装コード:依存関係スコアリング・品質管理エンジン

以下のコードをProjectの標準モジュールに配置し、実行することで、アクティブプロジェクト内の全タスクの依存関係を解析し、危険なタスクを抽出・ログ出力する。

Option Explicit

‘ ==============================================================================
‘ módulo名: modDependencyAnalyzer
‘ 概要: タスクの依存関係複雑度をスコアリングし、品質リスクを検知する
‘ 著者: チーフアーキテクト
‘ ==============================================================================

Public Sub AnalyzeTaskComplexity()
Dim prj As Project
Set prj = ActiveProject

‘ パフォーマンス最適化:画面描画と自動再計算を停止
Dim origScreenUp As Boolean
Dim origCalc As Long
origScreenUp = Application.ScreenUpdating
origCalc = Application.Calculation

Application.ScreenUpdating = False
Application.Calculation = pjManual

On Error GoTo ErrorHandler

Dim tsk As Task
Dim pred As Task
Dim succ As Task
Dim predColl As Dependencies
Dim succColl As Dependencies

Dim score As Double
Dim maxScore As Double
Dim bottleneckTaskName As String

‘ ログ出力用シートの準備(Excel連携の前提だが、今回はイミディエイトウィンドウに出力)
Debug.Print “=== Project Dependency Complexity Audit Start ===”
Debug.Print “Task ID, Task Name, Predecessors, Successors, Complexity Score, Status”

Dim thresholdScore As Double
thresholdScore = 15.0 ‘ 複雑度の警告閾値(独自定義)

For Each tsk in prj.Tasks
If Not tsk Is Nothing Then
‘ サマリータスクやマイルストーンの特殊処理(必要に応じて除外可能だが今回は全体評価)
If Not tsk.Summary Then

Dim predCount As Long
Dim succCount As Long
Dim lagComplexity As Long

predCount = 0
succCount = 0
lagComplexity = 0

‘ 先行タスクの解析
On Error Resume Next
Set predColl = tsk.Predecessors
If Err.Number = 0 And Not predColl Is Nothing Then
predCount = predColl.Count
Dim p As Dependency
For Each p in predColl
‘ ラグタイムが存在する場合は複雑度を係数として加点
If p.Lag <> 0 Then lagComplexity = lagComplexity + 1
Set p = Nothing
Next p
End If
Err.Clear

‘ 後続タスクの解析
Set succColl = tsk.Successors
If Err.Number = 0 And Not succColl Is Nothing Then
succCount = succColl.Count
Dim s As Dependency
For Each s in succColl
If s.Lag <> 0 Then lagComplexity = lagComplexity + 1
Set s = Nothing
Next s
End If
Err.Clear
On Error GoTo ErrorHandler

‘ ————————————————————–
‘ 【複雑度スコア算出アルゴリズム】
‘ スコア = (先行数 1.5) + (後続数 1.5) + (ラグ付き依存数 2.0)
‘ クリティカルパス上のタスクには 1.5倍の重み付けを行う
‘ ————————————————————–
score = (predCount 1.5) + (succCount 1.5) + (lagComplexity 2.0)
If tsk.Critical Then
score = score 1.5
End If

‘ 閾値超過タスクの検出とフラグ立て(カスタムフィールド等への書き込みも可能)
Dim statusMsg As String
If score >= thresholdScore Then
statusMsg = “【要警告】高複雑度”
‘ 例: タスクのテキストフィールド1に警告を書き込む
tsk.Text1 = “Complexity Risk: ” & Format(score, “0.0”)
Else
statusMsg = “正常”
tsk.Text1 = “”
End If

‘ 結果出力
Debug.Print tsk.ID & “, ” & _
“””” & tsk.Name & “””, ” & _
predCount & “, ” & _
succCount & “, ” & _
Format(score, “0.0”) & “, ” & _
statusMsg

‘ オブジェクトの明示的解放(メモリリーク防止の鉄則)
Set predColl = Nothing
Set succColl = Nothing
End If
End If
Next tsk

Debug.Print “=== Audit Completed Successfully ===”

CleanUp:
‘ 状態の復元
Application.ScreenUpdating = origScreenUp
Application.Calculation = origCalc
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “VBA Engine Error”
Resume CleanUp
End Sub

4. コードの解説とアーキテクトの視座

メモリ管理とCOM境界の制御

VBAからMS ProjectのCOMオブジェクトにアクセスする際、`For Each`ループ内で毎回生成されるラッパーオブジェクトは、VBAのガベージコレクションに任せるとメモリリークの原因となる。特に数万タスクを抱える大規模プロジェクトでは、`Set predColl = Nothing` や `Set p = Nothing` を明示的に記述し、ヒープ領域をクリーンに保つことがクラッシュを防ぐ唯一の手段である。

スコアリングモデルの妥当性

提案したスコア計算式:
$$\text{Score} = \left( (C_{\text{pred}} \times 1.5) + (C_{\text{succ}} \times 1.5) + (C_{\text{lag}} \times 2.0) \right) \times W_{\text{critical}}$$

  • 次数へのペナルティ: 1つのタスクにあまりに多くのリンクが集中している場合、それはWBSの粒度(Work Breakdown)が間違っている(細かすぎる、あるいは統合すべきである)ことの証明である。
  • ラグ(Lag)の危険性: ラグタイムはスケジュール管理における「見えない負債」である。理由の明確でないラグが多用されている依存関係は、スコアを跳ね上げ、自動スケジューリングの予測可能性を破壊する。

5. レガシー環境とシステム間連携への応用

このVBAスクリプト単体で完結させるだけでなく、これを基盤として「社内PMOシステムとの連携」へと昇華させることが、シニアエンジニアの腕の見所だ。

1. Active Directory / 外部DB連携: 抽出された「高複雑度タスク」の担当者に対し、自動的にOutlook経由で「WBS構造の見直しを促すアラートメール」を送信する。
2. CI/CDパイプラインとの統合: 毎週金曜日の深夜、Windowsタスクschedulerから無人実行(ヘッドレス)でMSPファイルを読み込み、このVBAスクリプトを走らせてプロジェクトの健康状態をJSON形式でダッシュボードサーバーにPOSTする。

WBSの美しさは、見た目のレイアウトではなく、その背後にある依存関係の「直交性とシンプルさ」に宿る。場当たり的な修正の繰り返しによって瓦解しかけたプロジェクトを、このコードで冷徹に診断し、真のコントロールを取り戻してほしい。

タイトルとURLをコピーしました