最有效卻無人使用的品質流程
1976年,Michael Fagan 在 IBM Systems Journal 上發表了一篇論文,描述了一種審查流程,其有效性使之成為軟體品質的金標準。Fagan Inspections 在尚未執行任何測試之前,就能捕獲全部缺陷的60%到90%。NASA 2002年的一項研究發現,每投入1小時審查,平均可避免後續33小時的維護工作。
按任何可衡量的標準,這都是軟體工程史上產出的最佳審查流程。
如今幾乎無人使用。
問題不在於 Fagan Inspections 是否有效。它有效得近乎過頭。問題在於,一個擁有如此業績的流程為何從主流開發中消失,而當我們取代它時,是否失去了某些重要的東西。
Fagan Inspections 的實際樣貌
Fagan 並非發明了 code review。他發明了一種特定的、高度結構化的發現缺陷的儀式。
該流程包含六個嚴格階段:
- Planning: 主持人選定參與者,並確認材料符合准入條件。
- Overview: 作者解釋背景。這是建立上下文,而非審查。
- Preparation: 每位參與者獨立審查材料,速度約為每小時150行,並列出個人懷疑的缺陷清單。
- Inspection Meeting: 團隊集會不超過兩小時。Reader 大聲朗讀邏輯。Recorder 記錄缺陷。主持人確保會議聚焦發現缺陷,而非解決缺陷。
- Rework: 作者修復所有已記錄的缺陷。
- Follow-up: 主持人驗證已做修正,且未引入新缺陷。
角色明確且不重疊。主持人負責推進流程,但不是技術權威。Reader 朗讀程式碼,但不為其辯護。作者在場,但會議期間禁止解釋意圖。目的不是協作,而是冷靜、系統化的缺陷偵測。
這正是它奏效的原因。普通工程會議的社會動力學被刻意排除在外。
為何數據如此出色
缺陷檢出率並非偶然。它源於若干深思熟慮的設計選擇。
Independent preparation 意味著六個人在群體討論可能對他們產生偏見之前,各自隔離地審查同一段程式碼。他們清單之間的重疊部分告訴你缺陷有多明顯。只有一個人發現的條目往往最有價值。
嚴格的兩小時上限防止疲勞摧毀判斷力。Fagan 知道審查有效性在兩小時後急劇下滑。每小時150行的速率同樣是刻意設定。速度再快,你開始看到的就不是實際存在的東西,而是你預期看到的東西。
No-solutions 規則保持會議聚焦。沒有什麼比滿屋子工程師為尚未完全界定的缺陷設計修復方案更能摧毀審查。
這些約束不是官僚負擔,而是機制本身。移除它們,你會得到更友好但也更無效的東西。
是什麼扼殺了 Fagan Inspections
如果流程如此有效,它為何消失?
簡短的答案是:它的昂貴方式正是現代軟體開發拒絕容忍的那種。
一次 Fagan Inspection 消耗被審程式碼編寫工時的15%到20%。在一個記錄在案的案例中,348行程式碼需要27.3人時的審查。當團隊每天多次發布時,這一比例不可想像。
光是日程安排就是一份全職工作。你需要五到六個人在房間裡待上兩小時,加上 preparation,加上 follow-up。在大型組織中,找到一個主持人、Reader、兩名 Reviewer、Recorder 和作者都有空的兩小時時段可能要花好幾天。
僵化的角色結構同樣無法擴展。Fagan Inspections 假定團隊穩定且人數足以填滿所有角色。在初創公司,五人團隊可能沒有專人能擔任專職主持人而不摧毀 velocity。
最大的因素是文化。Fagan Inspections 刻意製造不適。作者靜坐一旁,由同事朗讀其程式碼並記錄缺陷。沒有「這只是初稿」的餘地。該流程假定缺陷代價高昂,而社會摩擦成本低廉。現代工程運作的前提恰恰相反。
我們用什麼取代了它
業界並未放棄結構化審查。它用 pull request 取而代之。
Pull request review 是非同步的、低儀式感的,直接嵌入開發 workflow。Reviewer 可以在會議間隙、手機上或等待 CI 完成時查看 diff。沒有指定角色。作者和 Reviewer 往往是同一個人,只差一個 approval 就能 merge。
這在可及性、速度和 developer experience 上是巨大進步。在缺陷偵測上則是巨大退步。
多項研究發現,非正式審查大約只能捕獲結構化審查所捕獲缺陷的一半。2009年 Basili 等人的實驗對比了 Fagan 式審查與 lightweight review,發現更輕量的流程在同一材料上顯著檢出了更少的缺陷。
問題不在於 Reviewer 懶惰。流程的設計目的就不是發現缺陷。它的設計目的是讓程式碼在「多一雙眼睛看過」的情況下出貨,這不是一回事。
我們真正失去了什麼
Pull request review 優化吞吐率。Fagan Inspection 優化徹底性。這是截然不同的目標,兩者都沒錯。錯誤在於假設更輕量的流程是更重量流程的嚴格超集。
以下是消失的東西:
Independent preparation。 在 pull request 中,Reviewer 是 cold reading diff。他們沒有花一小時閱讀周邊上下文、追蹤資料流、建構心智模型。他們只是在回應通知。審查深度不可同日而語。
Reader 角色。 讓人大聲朗讀程式碼,迫使團隊以最慢成員能跟上的節奏推進。它暴露出默讀時隱藏的假設。螢幕上的 diff 讓眼睛跳過無聊的部分。Reader 不會跳過。
Defect-only focus。 Pull request 評論容易滑向風格和架構意見。這些也有價值,但不是缺陷偵測。花在爭論縮排上的每一分鐘,都是沒有花在尋找 null dereference 上的一分鐘。
可測量的流程資料。 Fagan Inspections 產出硬數字:每小時缺陷數、preparation 時間、rework 時間、模組級 defect density。現代審查工具只統計評論數和 approval 數,幾乎無法說明審查品質。
一種務實的中間路線
你不會在現代 continuous deployment 環境中執行完整的 Fagan Inspections。但你可以借用它重要的部分。
最重要的可遷移思想是結構化的 independent preparation。在深度非同步審查之前,要求 Reviewer 獨自花時間在材料上。不是快速掃讀。是真正的 preparation。
不要再用泛泛的 “LGTM”,強制推行一份 lightweight checklist,模仿 Fagan 內建在角色和規則中的紀律:
from dataclasses import dataclass, field
from typing import List, Optional
from enum import Enum
class DefectSeverity(Enum):
MINOR = "minor"
MAJOR = "major"
CRITICAL = "critical"
@dataclass
class ReviewEntry:
line_number: Optional[int]
category: str
severity: DefectSeverity
description: str
@dataclass
class InspectionReport:
reviewer: str
prep_time_minutes: int
entries: List[ReviewEntry] = field(default_factory=list)
def defect_count(self) -> int:
return len(self.entries)
def run_inspection_checklist(
code: str,
reviewer: str,
prep_time_minutes: int
) -> InspectionReport:
"""Structured prep produces structured output.
Mimics the Fagan prep phase: reviewer spends focused
time with the material, then logs findings against a
consistent taxonomy instead of ad hoc comments.
"""
report = InspectionReport(
reviewer=reviewer,
prep_time_minutes=prep_time_minutes
)
# Example: check for missing null handling
if "->" in code and "null" not in code.lower():
report.entries.append(ReviewEntry(
line_number=None,
category="null-safety",
severity=DefectSeverity.MAJOR,
description="No explicit null handling in pointer function"
))
return report
這不是 Fagan Inspection。它是恢復其最重要特性之一的方式:審查產出應當是結構化的、可測量的,並聚焦於缺陷類別而非個人意見。
另一個可遷移的思想是 time-boxed deep review。每個 sprint 挑選一個關鍵模組。安排一次90分鐘的聚焦審查,附帶 independent preparation。只記錄缺陷。不討論解決方案,不爭論風格,不涉及設計爭議。
成本是真實的。但如果 NASA 的比例哪怕大致成立,現在專注一小時就能省去日後數十小時的除錯。
令人不安的真相
Fagan Inspections 並未失敗。它們被拒絕,是因為其要求的嚴謹性與業界優先追求的速度不相容。
這一權衡對大量軟體而言是合理的。landing page 按鈕上的拼字錯誤不需要五人正式審查。但取代 Fagan Inspections 的文化對所有程式碼一視同仁,而成本就隱藏在這裡。
最昂貴的缺陷存在於那些看起來正確、通過測試、卻在生產環境中以真金白銀為代價的方式失敗的程式碼裡。正是這類程式碼,最能從「旨在發現缺陷」的流程中受益,而不是從「旨在 approve diff」的流程中受益。
Pull request review 將會繼續存在,這本身沒問題。但假裝它能取代結構化審查則有問題。它是為不同工作準備的不同工具,只擁有其一的團隊將繼續發現那些更優流程本可在出貨前捕獲的昂貴 bug。
FAQ
什麼是 Fagan Inspection?
一種由 Michael Fagan 於1970年代在 IBM 開發的、用於發現軟體製品缺陷的結構化多階段審查流程。包含六個階段(Planning、Overview、Preparation、Inspection Meeting、Rework、Follow-up),參與者有特定角色。會議只聚焦記錄缺陷,不解決缺陷。
Fagan Inspections 有多有效?
IBM 報告的缺陷移除率超過90%。NASA 2002年研究發現每1小時審查平均避免33小時維護。獨立研究一致表明,結構化審查捕獲的缺陷約為非正式審查的兩倍。
為什麼團隊不再使用 Fagan Inspections?
該流程消耗專案總工時的15%到20%,需要協調多名參與者的時間,且文化上過於僵化。隨著軟體團隊轉向更快的發布週期,開銷變得不可持續。Pull request review 取代它成為預設方案,因為它更快、更易融入日常工作流,儘管捕獲的缺陷更少。
現代團隊還能從 Fagan Inspections 中受益嗎?
不能以原始形式受益。帶指定角色的完整六階段流程不適合 continuous deployment。但核心思想——independent preparation、time-boxed focused review、structured defect logging、以及將缺陷發現與方案設計分離——可以改編。選擇性地將這些應用於關鍵程式碼的團隊,能在不產生開銷的情況下獲得大部分收益。