아무도 쓰지 않는 가장 효과적인 품질 프로세스
1976년 Michael Fagan은 IBM Systems Journal에 한 편의 논문을 발표했다. 거기에 기술된 리뷰 프로세스는 소프트웨어 품질의 금기준(gold standard)이 될 정도로 효과적이었다. Fagan Inspections는 단 하나의 테스트도 실행되기 전에 전체 결함의 60~90%를 포착했다. 2002년 NASA 연구에서는 인스펙션에 1시간을 투입할 때마다 평균 33시간의 유지보수 작업을 나중에 방지하는 것으로 나타났다.
어떤 측정 기준으로 보더라도 이것은 소프트웨어 공학이 지금까지 내놓은 최고의 리뷰 프로세스였다.
오늘날 거의 아무도 이를 사용하지 않는다.
문제는 Fagan Inspections가 작동했느냐가 아니다. 너무 잘 작동했다. 문제는 그런 실적을 가진 프로세스가 왜 주류 개발 현장에서 사라졌는지, 그리고 우리가 그것을 대체하면서 중요한 무언가를 잃지는 않았는지이다.
Fagan Inspections의 실제 모습
Fagan은 code review 자체를 발명한 것이 아니다. 결함을 찾기 위한 특정하고 고도로 구조화된 의식(ritual)을 발명한 것이다.
이 프로세스는 여섯 개의 엄격한 단계로 이루어져 있다.
- Planning: 중재자가 참가자를 선정하고 자료가 진입 기준(entry criteria)을 충족하는지 검증한다.
- Overview: 작성자가 배경을 설명한다. 이는 맥락을 설정하는 것이지 리뷰는 아니다.
- Preparation: 각 참가자가 자료를 독립적으로 검토한다. 대략 시간당 150줄의 속도로, 의심스러운 결함의 개인 목록을 만든다.
- Inspection Meeting: 팀이 2시간 이하로 모인다. Reader가 논리를 소리 내어 읽는다. Recorder가 결함을 기록한다. 중재자는 회의가 결함을 해결하는 것이 아니라 결함을 찾는 데 집중하도록 유도한다.
- Rework: 작성자가 기록된 모든 결함을 수정한다.
- Follow-up: 중재자가 수정 사항이 반영되었고 새로운 결함이 유입되지 않았는지 검증한다.
역할은 구체적이고 중복되지 않는다. 중재자는 프로세스를 운영하지만 기술적 권위자는 아니다. Reader는 코드를 읽어 주지만 옹호하지는 않는다. 작성자는 참석하지만 회의 중 의도를 설명하는 것이 금지된다. 목적은 협업이 아니다. 차갑고 체계적인 결함 탐지이다.
이것이 바로 프로세스가 작동한 이유이다. 일반적인 엔지니어링 회의의 사회적 역학은 의도적으로 제거되었다.
수치가 그토록 우수했던 이유
결함 탐지율은 우연이 아니었다. 몇 가지 의도적인 설계 선택에서 비롯되었다.
Independent preparation은 여섯 명이 그룹 토론의 영향을 받기 전에 동일한 코드를 고립되어 검토했다는 의미이다. 목록의 중복 부분은 결함이 얼마나 명백했는지를 알려준다. 한 사람만 찾아낸 항목이 종종 가장 가치 있었다.
2시간이라는 엄격한 상한은 피로가 판단력을 해치는 것을 막았다. Fagan은 인스펙션의 효율이 대략 2시간 이후에 절벽처럼 떨어진다는 것을 알고 있었다. 시간당 150줄이라는 속도 또한 의도적이었다. 더 빨리 가면 사람은 실제 존재하는 것이 아니라 기대하는 것을 보기 시작한다.
No-solutions 규칙은 회의의 집중력을 유지시켰다. 멤버 전원이 아직 특성화조차 되지 않은 결함의 수정안을 설계하기 시작하는 회의만큼 인스펙션을 빨리 망치는 것은 없다.
이러한 제약은 관료적 오버헤드가 아니라 메커니즘 자체였다. 이를 제거하면 더 친근하지만 덜 효과적인 것이 된다.
Fagan Inspections를 죽인 것
이토록 효과적인 프로세스가 왜 사라졌는가.
간단히 말해, 그것은 현대 소프트웨어 개발이 용납하지 않는 방식으로 비용이 많이 들었다.
한 번의 Fagan Inspection은 검토 대상 코드 작성에 소요된 노력의 15~20%를 소비했다. 기록된 사례 중 하나에서 348줄의 코드를 인스펙션하는 데 27.3인시가 필요했다. 팀이 하루에 여러 번 배포하는 상황에서 이 비율은 상상하기 어렵다.
스케줄 조정만으로도 상근 업무였다. 중재자, Reader, 2명의 Reviewer, Recorder, 작성자가 모두 참석 가능한 2시간짜리 시간대를 대규모 조직에서 찾는 데는 며칠이 걸릴 수도 있다. 여기에 preparation과 follow-up을 더해야 한다.
엄격한 역할 구조 또한 확장되지 않았다. Fagan Inspections는 모든 역할을 채울 수 있는 충분한 인원을 가진 안정된 팀을 전제로 한다. 5인 규모 스타트업에서는 전담 중재자를 두기 어려울 수 있다.
가장 큰 요인은 문화적이었다. Fagan Inspections는 의도적으로 불편했다. 작성자는 동료가 자신의 코드를 읽고 결함을 기록하는 동안 침묵하고 앉아 있어야 했다. “아직 초안일 뿐”이라는 여지가 없었다. 결함은 비싸고 사회적 마찰은 값싸다는 전제 위에 서 있다. 현대 엔지니어링은 정반대의 가정 위에서 작동한다.
우리가 대체한 것
업계는 구조화된 리뷰를 포기한 것이 아니다. pull request로 대체한 것이다.
Pull request review는 비동기식이고, 의식이 간소화되며, 개발 workflow에 직접 내장되어 있다. Reviewer는 회사 사이, 스마트폰으로, 또는 CI가 끝나기를 기다리는 동안 diff를 볼 수 있다. 할당된 역할은 없다. 작성자와 Reviewer는 merge를 위해 한 명의 승인이 더 필요한 동일 인물인 경우가 많다.
이는 접근성, 속도, developer experience 측면에서 엄청난 개선이다. 동시에 결함 탐지 측면에서 엄청난 퇴행이다.
여러 연구에서 비공식 리뷰가 구조화된 인스펙션이 포착하는 결함의 대략 절반만 포착한다는 것이 밝혀졌다. 2009년 Basili 등의 실험에서는 Fagan 스타일 인스펙션과 lightweight review를 비교했고, 가벼운 프로세스가 동일 자료에서 현저히 적은 결함을 포착했다.
문제는 Reviewer가 게으르다는 것이 아니다. 프로세스가 결함을 찾도록 설계되어 있지 않다. 두 번째 눈으로 코드를 훑어보고 출하할 수 있게 하는 데 설계되어 있으며, 이는 다른 문제이다.
우리가 실제로 잃은 것
Pull request review는 처리량(throughput)을 최적화한다. Fagan Inspection은 철저함을 최적화했다. 이들은 본질적으로 다른 목표이며, 어느 쪽이 틀렸다는 것이 아니다. 오류는 가벼운 프로세스가 무거운 프로세스의 엄격한 상위 집합이라고 가정하는 데 있다.
사라진 것은 다음과 같다.
Independent preparation. Pull request에서 Reviewer는 diff를 감으로 본다. 주변 맥락을 읽고, 데이터 흐름을 추적하고, 멘탈 모델을 구축하는 데 1시간을 들이지 않았다. 알림에 반응할 뿐이다. 검토의 깊이는 비교가 되지 않는다.
Reader 역할. 누군가 코드를 소리 내어 읽게 하면, 그룹은 가장 느린 사람도 따라갈 수 있는 속도로 진행해야 한다. 침묵 속 읽기가 숨기는 가정을 드러낸다. 화면의 diff는 눈이 지루한 부분을 건너뛰게 한다. Reader는 건너뛰지 않는다.
Defect-only focus. Pull request 코멘트는 스타일이나 아키텍처 의견으로 흐르기 쉽다. 그것들도 가치가 있지만, 결함 탐지는 아니다. 들여쓰기를 논하는 데 1분을 쓰는 것은 null dereference를 찾는 데 쓰지 못한 1분이다.
측정 가능한 프로세스 데이터. Fagan Inspections는 확실한 수치를 생산했다. 시간당 결함 수, preparation 시간, rework 시간, 모듈별 defect density. 현대 리뷰 도구는 코멘트와 승인 횟수를 세는데, 이는 리뷰 품질에 대해 거의 아무것도 말해주지 않는다.
실용적인 중간안
현대의 continuous deployment 환경에서 완전한 Fagan Inspections를 실행할 수는 없다. 하지만 중요한 부분은 차용할 수 있다.
가장 중요한 이식 가능한 아이디어는 구조화된 independent preparation이다. 깊은 비동기 리뷰 전에 Reviewer가 자료와 단독으로 시간을 보내도록 요구한다. 훑어보기가 아닌, 진정한 preparation이다.
“LGTM” 같은 일반적인 문구 대신, Fagan이 역할과 규칙에 담은 규율을 모방하는 lightweight checklist를 강제한다.
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이 아니다. 다만 그것의 가장 중요한 특성 중 하나를 되찾는 방법이다. 리뷰 산출물은 구조화되고 측정 가능하며, 의견이 아닌 결함 범주(defect category)에 초점을 맞춰야 한다는 것이다.
또 다른 이식 가능한 아이디어는 time-boxed deep review이다. 스프린트마다 핵심 모듈 하나를 고른다. Independent preparation을 갖춘 90분간의 집중 리뷰를 계획한다. 결함만 기록한다. 해결책 없이, 스타일 논쟁 없이, 설계 논쟁 없이.
비용은 현실이다. 하지만 NASA의 비율이 대략이라도 유지된다면, 지금 집중하는 1시간이 나중에 수십 시간의 디버깅을 절약한다.
불편한 진실
Fagan Inspections는 실패하지 않았다. 업계가 우선시하는 속도와 양립할 수 없는 엄격함 때문에 거부당한 것이다.
이러한 트레이드오프는 많은 소프트웨어에 대해 합리적이었다. landing page 버튼의 오타에 5인의 공식 인스펙션이 필요하지는 않다. 하지만 Fagan Inspections를 대체한 문화는 모든 코드를 똑같이 대하며, 그것이 비용이 숨어 있는 곳이다.
가장 비싼 결함은 올바르게 보이고, 테스트를 통과하며, 실제 금전적 손실을 초래하는 방식으로 본장에서 실패하는 코드 속에 있다. 바로 그 코드가, diff를 승인하도록 설계된 프로세스가 아니라 결함을 찾도록 설계된 프로세스로부터 가장 큰 이익을 얻는다.
Pull request review는 앞으로도 계속될 것이며, 그것 자체는 괜찮다. 하지만 그것이 구조화된 인스펙션의 대체물이라고 가장하는 것은 괜찮지 않다. 이는 다른 작업을 위한 다른 도구이며, 둘 중 하나만 가진 팀은 출하 전 더 나은 프로세스가 잡았을 비싼 버그를 계속 찾게 될 것이다.
FAQ
Fagan Inspection이란 무엇인가.
1970년대 IBM의 Michael Fagan이 개발한, 소프트웨어 산출물의 결함을 찾기 위한 구조화된 다단계 리뷰 프로세스. 6개의 단계(Planning, Overview, Preparation, Inspection Meeting, Rework, Follow-up)를 거치며 참가자에게 특정 역할이 부여된다. 회의는 결함 해결이 아닌 결함 기록에 전념한다.
Fagan Inspections는 얼마나 효과적이었는가.
IBM은 90%가 넘는 defect removal율을 보고했다. 2002년 NASA 연구에서는 인스펙션 1시간이 평균 33시간의 유지보수를 방지했다. 독립 연구에서는 일관되게 구조화된 인스펙션이 비공식 리뷰의 약 2배에 달하는 결함을 포착했다.
팀은 왜 Fagan Inspections 사용을 중단했는가.
이 프로세스는 총 프로젝트 노력의 15~20%를 소비하고, 다수 참가자의 어려운 일정 조율을 필요로 하며, 문화적으로 엄격했다. 소프트웨어 팀이 더 빠른 릴리스 주기로 전환하면서 오버헤드가 지속 불가능해졌다. Pull request review는 더 적은 결함을 포착하더라도 일반 workflow에 더 빠르고 쉽게 통합되므로 기본값으로 대체되었다.
현대 팀도 여전히 Fagan Inspections로부터 이익을 얻을 수 있는가.
원래 형태로는 아니다. 할당된 역할을 가진 완전한 6단계 프로세스는 continuous deployment에 맞지 않는다. 하지만 핵심 아이디어인 independent preparation, time-boxed focused review, structured defect logging, 그리고 결함 발견과 solution design의 분리는 적용 가능하다. 이를 중요 코드에 선택적으로 적용하는 팀은 오버헤드 없이 상당한 이익을 얻는다.