可維護性指數 (MI) 是軟體品質測量中最廣泛使用的綜合指標之一。它將程式碼的三個結構屬性——大小、複雜度和體積——濃縮成一個單一的數值分數,用於預測程式碼的修改難度。對於現代程式語言,其公式、閾值和工具都已相當成熟。但對於 COBOL 語言,情況則更為複雜,大多數團隊要么直接應用通用公式而不做任何調整,要么乾脆放棄量化測量,因為這些分數似乎與開發人員的實際體驗並不相符。
這兩種方法都會產生誤導性的結果。如果不了解 COBOL 各組成部分在 COBOL 語法環境中的行為方式,就將標準的 MI 公式應用於 COBOL,那麼得出的分數會存在系統性的偏差,導致高品質程式看起來差強人意,而低品質程序看起來可以接受。完全放棄測量則會使現代化決策缺乏必要的量化基礎,因此無法向業務利害關係人解釋其合理性,也無法在數千個程序中確定修復工作的優先順序。
正確的方法是理解 MI 公式在 COBOL 中具體衡量的是什麼,它在哪些方面低估或高估了質量,哪些補充指標可以彌補其 COBOL 特有的盲點,以及如何根據實際項目組合而不是從現代語言代碼庫中衍生出的通用基準來校準閾值。本指南涵蓋了所有這四個方面,既提供了足夠的技術深度來實施 COBOL MI 程序,也提供了足夠的實踐指導來將其應用於現代化決策。
在深入探討機制之前,最後一點是:可維護性指數(MI)試圖透過整合一系列不同的指標,全面展現項目不同部分的相對維護負擔。這種全面視角對於 COBOL 專案組合而言至關重要,因為沒有任何單一指標能夠全面反映情況。 MI 只是一個起點,而非全部,但卻是正確的切入點。
為什麼可維護性評估對 COBOL 語言比對現代語言更重要
對於一個已有兩年歷史、經過充分測試且由編寫團隊維護的 Java 或 Python 程式碼庫而言,評估其可維護性雖然能提供有用的信息,但通常並不迫切。程式碼對編寫者而言清晰易懂,邏輯也已記錄在案或可從測試中推導出來。變更成本僅限於團隊對程式碼的熟悉程度。
受監管行業的 COBOL 組合在三個方面有所不同,這使得定量可維護性測量不再是一種品質實踐,而是一種營運必需。
知識鴻溝。大多數 COBOL 程式碼都包含從未正式記錄過的業務邏輯。幾十年前編寫的批次作業中編碼的規則早已無人記得。能夠流暢閱讀程式碼並準確估算變更成本的開發人員正在退休。留下來的開發人員對部分項目也只有片面的了解。由於缺乏量化指標,變更成本的估算完全取決於你詢問的是哪位開發人員,而這些估算結果的差異之大,足以讓專案計畫變得不可靠。
規模問題。一個典型的 COBOL 程序組合包含數千個程序,其中許多程序多年未曾更新。任何團隊都不可能在現代化改造專案開始前手動評估數千個程序。如果能在數小時內自動計算整個程式組合的指標,就能取代耗時數週的手動審查。
商業論證要求。現代化專案需要投資論證。審批數百萬美元現代化預算的高階主管需要量化證據來證明投資的合理性。 MI評分、基於MI得出的技術債務比率以及參考MI閾值的變更成本估算,都能以易於非技術利益相關者理解的形式提供此類證據。
MI 公式及其 COBOL 特定組件
計算可維護性指數最常用的公式是:
MI = 171 - 5.2 × ln(Halstead Volume) - 0.23 × (Cyclomatic Complexity) - 16.2 × ln(Lines of Code)
微軟的有界變體(大多數商業工具都使用這種變體)將其映射到 0-100 的刻度:
MI (bounded) = max(0, (171 - 5.2 × ln(HV) - 0.23 × CC - 16.2 × ln(LOC)) × 100 / 171)
每個組件都有其特定的 COBOL 相關注意事項。
COBOL 程式碼行數
COBOL 原始檔包含四個部分:標識部分 (IDENTIFICATION)、環境部分 (ENVIRONMENT)、資料部分 (DATA) 和流程部分 (PROCEDURE)。標識部分用於標識程序。環境部分描述程式的執行環境。資料部分定義程式的資料結構。只有過程部分包含可執行語句。
測量問題: LOC 應該計算所有四個部門的所有行,還是只計算 PROCEDURE DIVISION 語句?
就維護性指標 (MI) 而言,統計所有程式碼行(包括資料聲明)會顯著增加程式碼行數 (LOC),而執行複雜度卻沒有相應增加。一個包含 400 行資料部 (DATA DIVISION) 定義記錄佈局和 100 行過程部 (PROCEDURE DIVISION) 的 COBOL 程序,與一個包含 100 行資料部和 400 行過程部的程序,其可維護性特徵截然不同,但僅從程式碼行數來看,它們卻被視為相同。
最佳實務:對於 COBOL 程式碼複雜度計算,應使用流程分部語句計數(不包括空白行、註解行和分部/節/段落標題),而不是原始程式碼總行數。這樣得到的程式碼行數值更能反映可執行檔的複雜度。
COPY 成員問題: COPY 語句會在編譯時包含外部來源成員。一條展開後包含 200 行資料定義的 COPY 語句,在原始檔中只增加一行,但在編譯後的程式中卻會增加 200 行。有些工具計算邏輯程式碼行數(複製後展開),而有些工具則計算物理原始碼行數。對於大量使用 COPY 語句的程序,這種差異可能高達一個數量級。
注意:如果您的 MI 工具統計的是實際原始碼行數,那麼大量使用 COPY 的程式看起來會比實際更小、更容易維護。請務必確認工具中的程式碼行數是 COPY 擴充之前還是之後的。
COBOL中的環路複雜度
圈複雜度是一種程式碼品質指標,它透過測量程式碼中獨立路徑的數量來衡量程式碼的可理解性和可維護性。在 COBOL 中,產生獨立路徑的決策結構包括:
| COBOL構造 | 環路複雜性影響 |
|---|---|
IF ... END-IF | 每IF+1 |
IF ... ELSE ... END-IF | 每個 IF 語句加 1(ELSE 語句不會加入其他路徑) |
EVALUATE ... WHEN | 每個 WHEN 子句 +1 |
PERFORM UNTIL condition | 每個 UNTIL 條件 +1 |
PERFORM VARYING ... WITH TEST BEFORE/AFTER | 每變化 +1 |
AT END 關於閱讀的條款 | +1 |
ON EXCEPTION / NOT ON EXCEPTION | 每個異常處理程序 +1 |
ON OVERFLOW / NOT ON OVERFLOW | 每個溢出處理程序 +1 |
ON SIZE ERROR | 每個大小錯誤處理程序 +1 |
88 級盲點: COBOL 的 88 級條件名稱建立了出現在 IF 和 EVALUATE 語句中的邏輯條件,但這些條件定義在資料部 (DATA DIVISION) 中。一個包含 20 個 88 級條件的程序,每個條件都被多個決策結構引用,其行為複雜度遠高於過程部 (PROCEDURE DIVISION) 的語句數量所顯示的複雜度。圈複雜度可以計算決策點的數量,但無法捕捉 88 級名稱與測試它們的邏輯之間的語義關係。
執行隱式複雜性: PERFORM SECTION-A THRU SECTION-Z 執行 SECTION-A 和 SECTION-Z 之間的所有段落。段落的數量以及其中的決策結構都是 PERFORM 語句有效複雜度的一部分,但語句層級的 CC 計算會考慮這些因素。 PERFORM THRU 無論中間經過什麼,它都是一條單一的路徑。
COBOL 中的 Halstead Volume
Halstead Volume 是衡量程式規模和複雜度的指標,它是基於運算符和操作數的數量。在 COBOL 中:
運算子是 COBOL 動詞和關鍵字:MOVE、ADD、SUBTRACT、MULTIPLY、DIVIDE、COMPUTE、IF、PERFORM、READ、WRITE、OPEN、CLOSE、CALL、GO TO、EVALUATE、WHEN 等。
操作數是資料名稱、字面值和圖形常數:在 DATA DIVISION 中定義的資料項目、數值和字串字面值,以及 COBOL 圖形常數(空格、零、高值、低值)。
冗長程度因素: COBOL 對於相同的邏輯比現代語言冗長得多。 Java 表達式 total = quantity * unitPrice * (1 - discount) 是一行包含四個運算子和四個運算元的程式碼。對應的 COBOL 程式碼如下:
科博爾
COMPUTE WS-TOTAL = WS-QUANTITY * WS-UNIT-PRICE
* (1 - WS-DISCOUNT)
在運算子和運算元方面,兩者大致等效,但考慮一個更複雜的計算:在 Java 中可能只需三行程式碼,而在 COBOL 中則可能需要五行或更多行,這是因為 COBOL 缺乏表達式鏈,並且需要使用中間工作儲存欄位。因此,COBOL 的 Halstead 值會比等效的 Java 值更高,原因很簡單,COBOL 用更多的語言標記來表達相同的計算。
實際後果是:COBOL 程式的 Halstead 值會高於邏輯相同的現代語言程式。較高的 Halstead 值會降低 MI 值。因此,COBOL 程式的 MI 分數會系統性地低於複雜度相同的現代語言程序,這並非因為它們更難維護,而是因為 COBOL 的語法更冗長。
標準 MI 在 COBOL 中的不足之處:四個盲點
即使計算正確,標準的 MI 公式也忽略了 COBOL 可維護性的四個維度,而這四個維度會對實際變更成本產生重大影響。
1. 複製成員耦合
一個被 300 個程式包含的 COBOL 副本構成維護依賴關係,任何對其的變更都會影響所有 300 個程式。這種耦合關係不會出現在 MI 公式的任何組成部分中。一個包含 20 個副本的程式有 300 個隱含依賴關係,MI 將其視為與不包含任何副本的程式等效。
補充指標:複製成員依賴計數,即程式資料部中唯一 COPY 語句的數量。對於 COPY 耦合度高的程序,在進行任何更改之前都需要進行影響分析,以了解哪些其他程序共享相同的副本定義。
2. 扇入(按計數)
一個被 150 個其他程序呼叫的 COBOL 子程序,無論其維護指數 (MI) 得分如何,都是一個高風險的變更目標。一個維護性極佳(MI = 85)但被 150 個程序調用的子程序,比一個維護性極差(MI = 45)且無人調用的實用程序更難安全地進行變更。維護指數 (MI) 公式並未考慮程序的使用範圍。
補充指標:扇入,指透過 CALL 或動態調度呼叫給定程式的不同程式的數量。扇入是子程序變更風險的主要驅動因素,與子程序的內部複雜性無關。
3. JCL依賴深度
一個由JCL作業呼叫的COBOL程序,如果其下游有15個依賴作業(即在其之後運行並依賴其輸出的作業),則其運行風險完全超出了MI的評估範圍。一個MI值為55且獨立運作的程序,其修改風險低於一個MI值為80且位於複雜批次依賴鏈中心的程序。
補充指標: JCL 依賴深度,即 JCL 作業網路中下游依賴鏈的深度。 JCL 依賴深度高的程序,無論其內部 MI 得分如何,任何更改都需要更廣泛的測試範圍。
4. 死代碼膨脹
死段落和死程式碼節,即已定義但從未被任何執行路徑呼叫的 COBOL 程式碼,會增加程式碼行數 (LOC) 和 Halstead 值,而不會增加運行程式碼的維護負擔。一個包含 600 行死代碼和 200 行運行代碼的程序,其管理資訊 (MI) 會因死代碼而對其進行懲罰,即使這些死代碼與變更成本無關。
補充指標:死程式碼百分比,指無法從任何生產執行路徑存取的 PROCEDURE DIVISION 語句的比例。較高的死代碼百分比表示 MI 計算的 LOC 和 Halstead 體積被顯著高估。
一套完整的 COBOL 指標套件
沒有哪一項單一指標能夠全面反映 COBOL 的可維護性。以下指標組合使用,才能提供完整的概覽:
| 公制 | 它衡量什麼 | COBOL 特定說明 | 主要用途 |
|---|---|---|---|
| 可維護性指標 | 整體維護便利性(複合材料) | 適用於程序部門聲明;核實副本處理 | 基準品質評分;投資組合排名 |
| 圈複雜度 | 獨立執行路徑的數量 | 包括 EVALUATE WHEN、PERFORM UNTIL、AT END、ON EXCEPTION | 每個專案的變更工作量;測試案例數量估算 |
| 哈爾斯特德卷 | 計算負載(運算子 + 操作數) | 預期價值高於同等現代語言課程 | MI 的一部分;COBOL 產品組合內的跨程式比較 |
| 複製會員數量 | 透過共享定義實現依賴耦合 | 擁有超過 15 名 COPY 成員的專案在進行任何變更之前都需要進行影響分析。 | 風險分類變更 |
| 扇入(呼叫) | 有多少程式會呼叫這個 | 子專案變更風險的主要驅動因素 | 遷移排序;變更授權閾值 |
| 死碼百分比 | 無法執行的程式碼百分比 | 如果未排除,則增加 LOC/HV;從轉換範圍中排除 | 現代化範圍縮減 |
| JCL依賴深度 | 下游批次作業鏈深度 | 僅憑 COBOL 原始碼無法計算;需進行 JCL 分析。 | 營運變更風險;測試範圍 |
| 嵌套 PERFORM 深度 | PERFORM 呼叫的最大嵌套級別 | 深層嵌套顯示結構複雜性未被CC所捕捉。 | 重構優先權 |
COBOL閾值校準
標準 MI 閾值源自現代語言程式碼庫,並非直接適用於 COBOL。下表比較了標準閾值與適用於 COBOL 的等效閾值,並解釋了調整的理由。
| 分數範圍 | 標準解釋 | COBOL 解釋 | 合理 |
|---|---|---|---|
| 85-100 | 易於維護 | 易於維護(一致) | 最佳 COBOL 程式得分在此範圍內,結構清晰,大小適中。 |
| 65-84 | 維護難度適中 | 維護性中等,請檢查 COPY 耦合和扇入 | 標準閾值仍然適用,但補充指標在這裡更為重要。 |
| 50-64 | 程式碼品質差,需要重構。 | 邊緣性,需結合具體情況評估 | 許多結構良好的 COBOL 程式僅憑冗長的程式碼就能在這裡得分;使用 CC 和扇入功能可以區分真正的問題和語法錯誤。 |
| 25-49 | 很差 | 品質差,可能有高CC值和/或過多LOC值的情況 | 這個範圍內的程式能夠可靠地指示結構性問題,而不僅僅是 COBOL 程式碼冗長。 |
| 0-24 | 關鍵性、重大重構 | 至關重要,必須優先進行補救或退役 | 與標準解釋一致 |
關鍵校準指南:在設定特定程式的閾值之前,請對整個 COBOL 程式碼庫執行 MI 分析。計算程式碼庫的中位數和四分位距。將「所需關注度」閾值設定為程式碼庫中處於最低四分之一的程式的第 25 個百分位數,而不是低於從 Java 程式推導出的閾值的程式。這種方法具有自校準功能,並考慮了 COBOL 程式碼系統性的冗長性。
利用管理資訊進行現代化決策
MI評分在引導具體營運決策時最有價值。以下是其主要應用場景。
遷移波次順序。 MI評分高的程序(維護良好、複雜度低)最適合早期遷移。它們更容易驗證,不太可能包含未記錄的邊界情況,並且在遷移環境中產生意外行為的風險也更低。 MI 評分低的程序應該在後期遷移,前提是團隊已經累積了經驗和信心,並且業務邏輯已經完全提取。
維護優先排序。維護資訊 (MI) 低於 25% 分位數且扇入度高(許多程序都這樣稱呼)或 JCL 依賴深度高的程序,代表最高風險的組合:結構複雜且許多其他程序都依賴這些程序。這些程序最容易產生與變更相關的缺陷,而且一旦發生缺陷,修復成本也最高。它們應該是技術債削減計劃的首要目標。
自建還是購買決策。在評估是長期維護 COBOL 程序還是用 SaaS 或更現代的替代方案替換它時,維護指數 (MI) 和變更成本歷史記錄為自建與購買的計算提供了量化依據。例如,一個 MI 值為 30 的程序,在過去三年中修改了 15 次,每次修改耗時都遠超預期,其維護成本已記錄在案,可以與替換成本進行比較。
變更授權閾值。一些組織使用 MI 分數來確定所需的變更授權等級。低於特定 MI 閾值的程序在生產部署前需要更嚴格的審查、獨立測試和額外的簽署確認。這創建了一個注重品質的變更控制流程,而無需對每次變更進行手動評估。
MI 唯一無法做到的是:預測變更對業務的影響。一個 MI 值為 85 的程序,如果執行監管資本計算,其所需的測試和驗證工作量至少與一個 MI 值為 40 的程序(執行低風險的報告功能)一樣多。 MI 衡量的是變更所需的工作量,而不是變更的後果。完整的風險評估需要同時考慮這兩個維度。
SMART TS XL 建立並追蹤 COBOL 可維護性指標
計算單一 COBOL 程式的管理資訊 (MI) 很簡單。但要準確計算包含數千個程式的管理信息,同時還要考慮 COPY 擴展、識別死代碼,並用 MI 遺漏的其他指標進行補充,則需要大規模的自動化分析。
SMART TS XL“ 靜態程式碼分析 本指南中所述的完整 COBOL 指標套件會同時在整個產品組合中計算。 MI 的計算是基於 PROCEDURE DIVISION 語句計數,而非原始碼總行數,並在分析前執行 COPY 展開,以確保共享資料定義得到正確歸屬。圈複雜度考慮了 EVALUATE WHEN 子句、PERFORM UNTIL 條件和異常處理程序分支,而不僅僅是 IF 語句。 Halstead Volume 是根據 PROCEDURE DIVISION 中的 COBOL 動詞和資料運算元計算得出的。
至關重要的是 SMART TS XL 它補充了MI公式中缺少的指標。 應用程式依賴關係映射 產生投資組合中每個項目的扇入值和呼叫次數,從而識別高風險項目,無論其 MI 評分為何。 JCL擴充 此功能為每個程式提供 JCL 依賴關係深度,將 COBOL 層級的 MI 分析與只有 JCL 分析才能揭示的運行風險背景連結起來。
影響分析功能中的死代碼識別會標記沒有入站執行路徑的段落和章節,從而可以將死代碼從 MI 計算中排除,並提供死代碼百分比指標,以確定程序的低 MI 分數反映的是真正的複雜性還是虛高的 LOC。
企業級搜尋功能使完整的指標資料集可查詢:只需一次查詢,即可在數百萬行 COBOL 程式碼中,找到所有 MI 低於 40 且扇入高於 20 的程序,並按 JCL 依賴深度排序,以及組合中優先級最高的修復目標。此可查詢的指標清單是上述維護優先排序、遷移排序和變更授權應用程式的基礎。
最後,MI 隨時間推移進行跟踪,並在每個重大變化週期後重新計算,以顯示該程式是在改進還是在退化。 SMART TS XL此分析基於當前原始程式碼而不是快取快照運行,從而確保指標反映每次分析時程式碼庫的實際狀態,而不是過時的某個時間點的評估。
與語言相符的指標
可維護性指數 (MI) 是 COBOL 程式碼組合的一個有效且有價值的指標,但前提是必須理解 COBOL 的語法特性與其元件之間的互動方式。將通用閾值應用於 COBOL 程序會產生誤導性的結果。透過補充 MI 所忽略的指標,例如 COPY 耦合、扇入、JCL 依賴深度和死程式碼百分比,可以更準確地反映開發人員在 COBOL 程式碼組合中實際遇到的情況。
能夠有效利用這些指標的組織,會將它們視為展開深入討論的起點,而非對專案品質的最終評判。 MI 分數只是一個訊號。這個訊號值得關注,它總是指向那些變更成本高昂、修改風險大,且需要在現代化改造開始前加以解決的專案。 MI 無法取代開發人員對程式碼的解讀,也無法取代結構分析——後者能夠揭示任何單一指標都無法捕捉到的依賴關係和業務邏輯。


