Skip to content
Published on

인식론과 소프트웨어 엔지니어링: 기술적 확신의 함정과 모른다는 것의 가치

Authors
  • Name
    Twitter

인식론과 소프트웨어 엔지니어링

서론: 우리는 정말로 "알고 있는가"

소프트웨어 엔지니어는 매일 "안다"는 판단 위에서 일한다. 이 코드가 무엇을 하는지 안다. 이 시스템이 어떻게 동작하는지 안다. 이 아키텍처가 왜 올바른지 안다. 하지만 그 "앎"은 정말로 앎인가?

2,500년 전 소크라테스는 "나는 내가 모른다는 것을 안다"라고 말했다. 이 겸손한 고백은 서양 철학 인식론(Epistemology)의 출발점이 되었다. 인식론은 지식의 본질, 범위, 한계를 탐구하는 철학의 한 분야로, "무엇을 알 수 있는가", "앎이란 무엇인가", "우리의 믿음은 정당화되는가"라는 근본적 질문을 던진다.

놀랍게도 이 고대의 질문들은 현대 소프트웨어 엔지니어링의 핵심 문제와 정확히 맞닿아 있다.

"프로그램 테스팅은 버그의 존재를 보여줄 수 있지만, 버그의 부재를 증명할 수는 없다." - Edsger Dijkstra, "On the Cruelty of Really Teaching Computing Science" (1988)

Dijkstra의 이 유명한 경고는 본질적으로 인식론적 주장이다. 소프트웨어의 정확성에 대한 우리의 지식은 근본적으로 불완전하다는 것이다.

이 글에서는 인식론의 주요 개념들을 소프트웨어 엔지니어링에 적용하여, 기술적 확신의 함정을 분석하고, "모른다"는 것을 인정하는 문화가 왜 더 나은 소프트웨어를 만드는지 탐구한다.


1장: 인식론의 핵심 개념과 소프트웨어 엔지니어링

인식론이란 무엇인가

인식론(Epistemology)은 그리스어 episteme(지식)와 logos(학문)의 합성어로, 지식에 대한 학문이다. 플라톤은 지식을 "정당화된 참인 믿음(Justified True Belief)"으로 정의했다. 이 정의에 따르면, 무언가를 "안다"고 말하려면 세 가지 조건이 충족되어야 한다.

  1. 믿음(Belief): 주체가 해당 명제를 믿어야 한다
  2. 진리(Truth): 해당 명제가 실제로 참이어야 한다
  3. 정당화(Justification): 그 믿음이 합리적 근거에 의해 뒷받침되어야 한다

이 세 조건을 소프트웨어 엔지니어링에 대입해보면 흥미로운 통찰이 나온다.

소프트웨어 엔지니어링에서 "안다"는 것

인식론적 조건소프트웨어 엔지니어링 맥락흔한 착각
믿음"이 코드는 정확하게 동작한다"코드를 읽고 이해했다고 확신
진리모든 입력과 상태에서 실제로 올바르게 동작테스트한 케이스에서만 동작 확인
정당화충분한 테스트, 증명, 검증일부 테스트 통과를 전체 정확성의 근거로 사용

Daniel Kahneman은 저서 Thinking, Fast and Slow(2011)에서 인간의 사고를 **시스템 1(빠른, 직관적)과 시스템 2(느린, 분석적)**로 구분했다. 엔지니어들이 "이 코드가 맞다"고 판단할 때, 대부분 시스템 1이 작동한다. 코드를 훑어보고 패턴을 인식하고 직관적으로 "올바르다"는 판단을 내리는 것이다. 하지만 이 직관적 판단은 수많은 인지 편향에 취약하다.


2장: 세 가지 인식론적 착각

착각 1: 코드를 읽었다 = 이해했다 (가독성의 착각)

코드 리뷰에서 가장 흔한 함정은 **"읽었으니까 이해했다"**는 착각이다. Gerald Weinberg는 The Psychology of Computer Programming(1971)에서 이미 이 문제를 지적했다.

"프로그래밍에서 가장 위험한 순간은 자신이 프로그램을 이해했다고 확신하는 바로 그 순간이다." - Gerald Weinberg

이것은 인식론에서 말하는 **"이해의 착각(Illusion of Understanding)"**이다. 우리는 텍스트를 읽을 때 실제로 깊이 이해하지 않고도 이해했다는 느낌을 받는다. 코드도 마찬가지다.

가독성의 착각이 발생하는 메커니즘:

  • 패턴 매칭: 익숙한 코드 패턴을 보면 내용을 분석하지 않고 "아, 이건 이런 패턴이구나"로 넘어간다
  • 명명 편향: 변수명과 함수명이 의도를 잘 반영하면, 실제 구현을 검증하지 않고 이름만으로 판단한다
  • 확증 편향: PR 설명에서 "버그 수정"이라고 되어 있으면, 코드에서 버그가 수정된 부분만 눈에 들어온다
  • 앵커링 효과: 코드 작성자의 설명이 리뷰어의 판단을 고정시켜, 독립적인 분석을 방해한다

대응 전략:

  1. 코드를 읽기 전에 PR 설명을 먼저 읽지 않는다 (앵커링 방지)
  2. 핵심 로직은 직접 머릿속에서 실행해본다 (수동 트레이싱)
  3. "이 코드가 실패하려면 어떤 입력이 필요한가?"를 먼저 생각한다 (반증적 접근)
  4. 자신이 이해했다고 느끼는 순간, 한 번 더 의심한다

착각 2: 테스트가 통과했다 = 정확하다 (검증의 한계)

테스트 스위트가 모두 녹색이면, 코드가 정확하다고 믿고 싶어진다. 하지만 이것은 귀납법의 근본적 한계와 직결된다.

18세기 철학자 데이비드 흄(David Hume)은 귀납법의 문제를 명확히 지적했다. "과거의 경험이 미래를 보장하지 않는다." 1,000번 관찰한 백조가 모두 흰색이었다고 해서 "모든 백조는 흰색이다"라는 결론을 내릴 수 없다. 호주에서 검은 백조가 발견될 때까지는.

소프트웨어 테스트도 마찬가지다.

테스트 결과실제 의미흔한 오해
100개 테스트 통과100개의 특정 시나리오에서 기대대로 동작모든 시나리오에서 올바름
코드 커버리지 100%모든 코드 라인이 최소 1회 실행됨모든 실행 경로와 상태 조합이 검증됨
통합 테스트 통과특정 환경에서 컴포넌트 간 상호작용 확인모든 환경에서의 정합성 보장
성능 테스트 통과테스트 조건에서 기준치 충족프로덕션 부하에서의 성능 보장

검증 방법별 인식론적 한계:

  • 단위 테스트: 개별 함수의 입출력을 검증하지만, 함수 간 상호작용에서 발생하는 창발적 버그를 잡지 못한다
  • 통합 테스트: 컴포넌트 간 상호작용을 검증하지만, 실제 프로덕션 환경의 모든 변수를 재현하지 못한다
  • E2E 테스트: 사용자 시나리오를 검증하지만, 실행 시간과 비용 제약으로 모든 시나리오를 커버하지 못한다
  • 형식 검증(Formal Verification): 수학적 증명에 가장 가까우나, 명세 자체가 현실을 완벽히 반영하는지는 검증할 수 없다

착각 3: 프로덕션에서 동작한다 = 올바르다 (귀납법의 문제)

"2년째 프로덕션에서 잘 돌아가고 있어요." 이 문장은 엔지니어에게 가장 강력한 정당화 근거처럼 느껴진다. 하지만 이것은 전형적인 귀납적 추론의 오류다.

나심 탈레브(Nassim Taleb)는 저서 The Black Swan(2007)에서 "칠면조 문제"를 제시한다. 매일 먹이를 받는 칠면조는 1,000일 연속으로 먹이를 받으며 "주인은 나를 먹여주는 존재"라는 확신을 갖는다. 1,001일째, 추수감사절에 칠면조의 목이 잘린다. 1,000일간의 경험적 증거가 한순간에 무효화된다.

소프트웨어 시스템에서도 동일한 패턴이 반복된다.

  • 2년간 문제없던 날짜 처리 로직이 윤년에 장애를 일으킨다
  • 10년간 안정적이던 시스템이 트래픽이 특정 임계치를 넘는 순간 연쇄 장애를 일으킨다
  • "한 번도 문제없었던" 동시성 코드가 특정 타이밍에서 데이터를 손상시킨다

"동작한다"와 "올바르다"의 차이:

관점"동작한다""올바르다"
시간 범위지금까지 관찰한 기간모든 미래 포함
입력 범위지금까지 들어온 입력가능한 모든 입력
환경 범위현재 인프라와 설정가능한 모든 환경
보장 수준경험적(귀납적)논리적(연역적)
철학적 지위귀납적 일반화보편적 진리

3장: 데카르트의 방법적 회의와 코드 리뷰

방법적 회의란 무엇인가

르네 데카르트(1596-1650)는 방법서설(Discourse on the Method)(1637)에서 **방법적 회의(Methodical Doubt)**를 제시했다. 그의 접근법은 단순하지만 급진적이다: 의심할 수 있는 모든 것을 의심하고, 의심할 수 없는 것만 남겨라.

데카르트는 감각, 기억, 논리적 추론까지 모두 의심했고, 결국 "의심하고 있는 나 자신의 존재"만은 의심할 수 없다는 결론에 도달했다. 유명한 "Cogito, ergo sum(나는 생각한다, 고로 존재한다)"이다.

코드 리뷰에 적용하는 방법적 회의

데카르트의 방법적 회의를 코드 리뷰에 적용하면, 다음과 같은 질문 프레임워크가 나온다.

1단계: 전제를 의심하라

  • 이 코드가 해결하려는 문제가 정말로 존재하는가?
  • 요구사항 자체가 정확한가?
  • 우리가 가정하고 있는 사용자 행동이 실제와 일치하는가?

2단계: 구현을 의심하라

  • 이 알고리즘이 모든 엣지 케이스에서 동작하는가?
  • 에러 처리가 모든 실패 경로를 커버하는가?
  • 동시성 문제, 리소스 누수, 정수 오버플로우는 없는가?

3단계: 검증을 의심하라

  • 테스트가 실제로 의미 있는 것을 검증하고 있는가?
  • 테스트 자체에 버그가 없는가?
  • 테스트 환경이 프로덕션을 충분히 반영하는가?

4단계: 자기 자신을 의심하라

  • 내가 이 코드를 이해했다는 확신은 정당한가?
  • 나의 판단에 영향을 미치는 편향은 없는가?
  • 이 리뷰에 충분한 시간과 집중력을 투자했는가?

방법적 회의의 실전 체크리스트

회의 단계질문확인 방법
전제 의심문제 정의가 정확한가?요구사항 문서와 대조
전제 의심가정이 명시적인가?코드 주석과 문서 확인
구현 의심경계값은 처리되는가?경계값 테스트 확인
구현 의심실패 경로는 안전한가?에러 핸들링 추적
검증 의심테스트가 의미있는가?뮤테이션 테스팅 고려
자기 의심편향에 빠지지 않았는가?의식적 재검토

4장: 칼 포퍼의 반증주의와 테스트 주도 개발

반증주의의 핵심

칼 포퍼(Karl Popper)는 20세기 가장 영향력 있는 과학 철학자 중 한 명이다. 그의 대표작 The Logic of Scientific Discovery(1934)에서 제시한 **반증주의(Falsificationism)**는 과학과 비과학을 구분하는 기준을 제공한다.

포퍼에 따르면, 과학적 이론은 **반증 가능(Falsifiable)**해야 한다. 즉, 어떤 관찰이나 실험이 그 이론을 틀렸다고 입증할 수 있어야 한다. "모든 백조는 흰색이다"는 과학적 주장이다. 검은 백조 한 마리로 반증할 수 있기 때문이다. 반면 "신은 존재한다" 또는 "모든 것에는 이유가 있다"는 반증이 불가능하므로 과학적 주장이 아니다.

TDD는 반증주의의 실천이다

테스트 주도 개발(TDD)은 놀라울 정도로 포퍼의 반증주의와 닮아 있다.

반증주의TDD
가설을 세운다기능 요구사항을 정의한다
반증 가능한 예측을 도출한다실패하는 테스트를 먼저 작성한다 (Red)
실험으로 가설을 시험한다코드를 작성하여 테스트를 통과시킨다 (Green)
가설이 살아남으면 잠정적으로 수용한다리팩토링하고 테스트가 여전히 통과하는지 확인한다 (Refactor)
새로운 반증 시도를 계속한다새로운 테스트 케이스를 추가한다
반증되면 가설을 수정한다테스트가 실패하면 코드를 수정한다

포퍼는 "이론은 결코 증명될 수 없다. 다만 반증되지 않은 채로 남아 있을 뿐이다"라고 말했다. 마찬가지로 Dijkstra가 말했듯이, 테스트는 코드의 정확성을 증명하지 못한다. 다만 아직 반증되지 않은 상태를 유지할 뿐이다.

반증 가능한 요구사항 작성법

포퍼의 반증주의는 요구사항 작성에도 적용된다. 반증 가능한 요구사항은 명확한 테스트 기준을 내포한다.

반증 불가능한(나쁜) 요구사항:

  • "시스템은 빨라야 한다" -- 얼마나 빨라야 하는지 기준이 없다
  • "사용자 경험이 좋아야 한다" -- "좋다"의 기준이 불명확하다
  • "안정적으로 동작해야 한다" -- "안정적"의 정의가 없다

반증 가능한(좋은) 요구사항:

  • "API 응답 시간이 p99 기준 200ms 이하여야 한다" -- 200ms를 초과하면 반증됨
  • "로그인 프로세스가 3단계 이내로 완료되어야 한다" -- 4단계 이상이면 반증됨
  • "서비스 가용성이 월간 99.9% 이상이어야 한다" -- 99.9% 미만이면 반증됨

Dijkstra의 경고를 다시 읽다

Dijkstra는 1988년 논문 "On the Cruelty of Really Teaching Computing Science"에서 소프트웨어 테스팅의 근본적 한계를 다음과 같이 설명했다.

"프로그램 테스팅은 버그의 존재를 설득력 있게 보여줄 수 있지만, 버그의 부재를 결코 보여줄 수 없다."

이것은 포퍼의 반증주의를 정확히 반영한다. 테스트가 실패하면 버그의 존재가 반증 불가능하게 확인된다. 그러나 테스트가 통과해도 버그의 부재가 증명되는 것은 아니다. 우리가 할 수 있는 최선은 더 많은 반증 시도(테스트)를 통해 코드의 신뢰도를 점진적으로 높이는 것뿐이다.


5장: 비교표 - 인식론적 관점에서 본 소프트웨어 검증 방법

종합 비교: 검증 방법과 인식론적 강도

검증 방법인식론적 유형확신도커버리지비용한계
코드 리뷰사회적 인식론 (합의)낮음~중간의존적낮음리뷰어의 역량과 편향에 의존
단위 테스트귀납적 추론중간좁음낮음상호작용과 통합 문제 미검출
통합 테스트귀납적 추론중간중간중간환경 차이로 인한 간극
E2E 테스트귀납적 추론중간~높음넓음높음느리고, 취약하며, 모든 경로 커버 불가
정적 분석연역적 추론높음규칙 범위 내낮음규칙으로 표현 가능한 문제만 검출
형식 검증연역적 증명매우 높음명세 범위 내매우 높음명세 자체의 정확성은 보장 불가
프로덕션 모니터링경험적 관찰사후적실제 사용 범위중간이미 발생한 문제만 감지
카오스 엔지니어링실험적 반증높음장애 시나리오높음설계한 시나리오 범위에 한정

검증 방법의 인식론적 계층

검증 방법을 인식론적 강도 순으로 배치하면, 피라미드 형태가 나타난다.

레벨 1 - 믿음(Belief): 코드 작성자의 자기 확신. "나는 이 코드가 맞다고 믿는다." 인식론적으로 가장 약하다.

레벨 2 - 사회적 합의(Social Consensus): 코드 리뷰를 통한 동료의 동의. "우리 팀이 이 코드를 검토하고 승인했다." 다수의 관점이 반영되지만, 집단 사고(Groupthink)에 취약하다.

레벨 3 - 경험적 증거(Empirical Evidence): 테스트 통과와 프로덕션 운영 경험. "이 코드는 1,000개의 테스트를 통과했고, 6개월간 프로덕션에서 동작했다." 강력한 귀납적 증거이지만, 미관찰 영역의 문제를 보장하지 못한다.

레벨 4 - 논리적 증명(Logical Proof): 형식 검증과 수학적 증명. "이 알고리즘이 올바름을 수학적으로 증명했다." 가장 강력하지만, 적용 범위가 제한적이고 비용이 매우 높다.

핵심 통찰은, 대부분의 소프트웨어 개발은 레벨 2~3에서 이루어진다는 것이다. 우리는 절대적 확실성이 아닌 "충분한 확신"의 영역에서 일하고 있으며, 이것을 인정하는 것이 인식론적 겸손의 출발점이다.


6장: 더닝-크루거 효과와 기술적 겸손

더닝-크루거 효과의 본질

1999년 심리학자 David Dunning과 Justin Kruger는 코넬 대학교에서 수행한 실험 결과를 발표했다. "Unskilled and Unaware of It: How Difficulties in Recognizing One's Own Incompetence Lead to Inflated Self-Assessments"라는 제목의 이 논문은 인지 편향의 역사에서 가장 널리 인용되는 연구 중 하나가 되었다.

더닝-크루거 효과의 핵심은 다음과 같다.

  • 능력이 낮은 사람은 자신의 능력을 과대평가한다: 자신이 무엇을 모르는지 모르기 때문이다
  • 능력이 높은 사람은 자신의 능력을 과소평가한다: 자신이 아는 것이 당연하다고 생각하기 때문이다
  • 능력이 향상되면 자기 평가가 더 정확해진다: 메타인지(자신의 인지에 대한 인지)가 발달하기 때문이다

소프트웨어 엔지니어링에서의 더닝-크루거

이 효과는 소프트웨어 엔지니어링에서 매우 뚜렷하게 관찰된다.

경력 단계전형적 자기 인식실제 역량인식론적 특성
주니어 (0-2년)"나는 꽤 잘하는 편이다"기본기 학습 중모르는 것의 범위를 모름
중급 (2-5년)"내가 정말 모르는 게 많구나"실무 역량 성장 중무지의 범위를 인식하기 시작
시니어 (5-10년)"알면 알수록 모른다"깊은 전문성불확실성을 수용하고 활용
스태프+ (10년+)"상황에 따라 다르다"넓은 맥락적 판단절대적 해답의 부재를 인정

주니어 개발자가 "이 아키텍처가 최고야"라고 확신에 찬 목소리로 말할 때, 시니어 개발자가 "글쎄, 우리 상황에서는 이런 트레이드오프가 있고..."라고 조심스럽게 말하는 이유가 여기에 있다. 시니어의 조심스러움은 무능이 아니라 메타인지의 발달이다.

"I Don't Know"의 가치

소프트웨어 엔지니어링 문화에서 "모른다"는 말은 종종 약점으로 인식된다. 특히 시니어 엔지니어나 리드에게 "모른다"는 고백은 권위의 상실처럼 느껴질 수 있다. 하지만 인식론적 관점에서, "모른다"는 말은 지식의 정직한 상태 표현이다.

"모른다"가 가치 있는 이유:

  1. 탐색을 시작하게 한다: "안다"는 확신은 탐색을 중단시키지만, "모른다"는 인정은 조사를 시작하게 한다
  2. 집단 지성을 활성화한다: 한 사람이 모른다고 인정하면, 다른 사람들이 자신의 지식을 공유하기 시작한다
  3. 잘못된 결정을 예방한다: 불완전한 지식에 기반한 자신감 넘치는 결정보다, 불확실성을 인정한 신중한 결정이 더 안전하다
  4. 학습 문화를 만든다: "모른다"가 안전한 환경에서는 질문과 학습이 활발해진다

"모른다"를 생산적으로 말하는 방법:

  • "이 부분은 제가 확실하지 않아서, 확인 후 공유하겠습니다"
  • "제 이해가 맞는지 확인이 필요합니다. 같이 검토해볼 수 있을까요?"
  • "이 영역은 제 전문 분야가 아닌데, 팀에서 더 잘 아시는 분이 계실까요?"
  • "직감적으로는 A가 맞을 것 같지만, 데이터를 보기 전까지는 확신할 수 없습니다"

7장: 베이지안 추론과 기술적 의사결정

베이지안 사고방식

토머스 베이즈(Thomas Bayes, 1701-1761)의 이름을 딴 베이지안 추론은 새로운 증거에 기반하여 믿음의 확률을 업데이트하는 방법이다. 핵심 공식은 다음과 같다.

P(A|B) = P(B|A) x P(A) / P(B)

여기서 P(A)는 사전 확률(Prior), P(A|B)는 새 증거 B를 관찰한 후의 사후 확률(Posterior), P(B|A)는 우도(Likelihood)이다.

Daniel Kahneman은 Thinking, Fast and Slow에서 인간이 베이지안 업데이트를 자연스럽게 수행하지 못한다고 지적했다. 우리는 새로운 증거에 과잉 반응하거나 과소 반응하는 경향이 있다.

장애 원인 분석에서의 베이지안 사고

프로덕션 장애가 발생했을 때, 베이지안 추론은 원인 분석의 강력한 프레임워크를 제공한다.

시나리오: API 응답 시간이 갑자기 10배 증가했다.

1단계: 사전 확률 설정 (경험과 통계 기반)

가능한 원인사전 확률근거
데이터베이스 부하35%과거 장애의 가장 흔한 원인
네트워크 문제20%인프라 관련 장애 빈도
코드 배포 이슈25%최근 배포가 있었음
외부 서비스 장애15%외부 의존성 존재
리소스 고갈5%드물지만 가능

2단계: 증거 수집과 확률 업데이트

증거 1: "최근 30분 이내에 배포가 있었다" -- 코드 배포 이슈의 확률이 상승한다 (25% -> 45%)

증거 2: "DB 쿼리 응답 시간은 정상이다" -- 데이터베이스 부하의 확률이 하락한다 (35% -> 5%)

증거 3: "특정 API 엔드포인트에서만 느리다" -- 코드 배포 이슈의 확률이 더 상승한다 (45% -> 70%)

3단계: 업데이트된 확률 기반 행동

가능한 원인업데이트된 확률조치 우선순위
코드 배포 이슈70%1순위: 최근 배포 변경사항 확인
외부 서비스 장애15%2순위: 외부 서비스 상태 확인
네트워크 문제7%3순위: 네트워크 메트릭 확인
데이터베이스 부하5%4순위: DB 상세 모니터링 확인
리소스 고갈3%5순위: 서버 리소스 확인

일상적 기술 의사결정에서의 베이지안 사고

베이지안 사고는 장애 분석뿐 아니라 일상적인 기술 의사결정에도 적용된다.

기술 선택:

  • 사전 확률: "이 프레임워크가 우리 프로젝트에 적합할 확률은 60%이다" (커뮤니티 평가, 문서 품질 기반)
  • 새 증거: 비슷한 규모의 팀에서 성공적으로 사용했다는 사례 발표
  • 사후 확률: 적합 확률이 60%에서 75%로 상승
  • 새 증거: 우리 팀의 기술 스택과의 호환성 테스트에서 문제 발견
  • 사후 확률: 적합 확률이 75%에서 40%로 하락

핵심은 증거에 따라 판단을 유연하게 업데이트하는 것이다. 처음 판단에 고착되지 않고, 새로운 정보가 들어올 때마다 확률을 조정하는 습관이 더 나은 기술적 의사결정으로 이어진다.


8장: 나심 탈레브의 안티프래질과 시스템 설계

프래질, 로버스트, 안티프래질

나심 탈레브는 저서 Antifragile: Things That Gain from Disorder(2012)에서 시스템을 세 가지 범주로 분류했다.

특성프래질(Fragile)로버스트(Robust)안티프래질(Antifragile)
정의충격에 의해 손상충격에 무관충격에 의해 강화
비유유리잔바위근육
불확실성에 대한 태도회피저항활용
실패 시깨짐견딤적응하며 성장

소프트웨어 시스템에 적용하기

프래질한 시스템의 특징:

  • 단일 장애점(Single Point of Failure)이 존재
  • 에러 처리가 없거나 최소한
  • 변경 시 예측 불가능한 부작용 발생
  • 장애에서 복구하는 메커니즘이 없음
  • 한 번의 장애로 전체 시스템이 마비

로버스트한 시스템의 특징:

  • 이중화(Redundancy)가 구현되어 있음
  • 에러를 감지하고 격리할 수 있음
  • 장애 시 폴백(Fallback) 메커니즘이 동작
  • 특정 범위의 충격까지는 견딜 수 있음
  • 하지만 예상 범위를 벗어나면 여전히 실패

안티프래질한 시스템의 특징:

  • 장애로부터 학습하여 자동으로 개선
  • 카오스 엔지니어링을 통해 의도적으로 장애를 주입하고 시스템을 강화
  • 실패한 부분이 자동으로 격리되고, 시스템이 스스로 재구성
  • 부하가 증가하면 자동으로 스케일 아웃
  • 과거의 장애 패턴을 학습하여 미래의 유사 장애에 대한 방어력 향상

불확실성을 이용하는 설계 vs 불확실성을 거부하는 설계

설계 접근법불확실성 거부 (프래질)불확실성 이용 (안티프래질)
에러 처리"이 에러는 발생하지 않을 것""모든 에러는 발생할 수 있다"
용량 계획정확한 예측에 기반자동 스케일링과 탄력성에 기반
배포 전략빅뱅 배포카나리, 블루-그린, 점진적 배포
장애 대응장애 발생 후 사후 대응카오스 엔지니어링으로 사전 탐색
아키텍처모놀리식, 강결합느슨한 결합, 서킷 브레이커
데이터단일 데이터베이스다중 저장소, 이벤트 소싱
팀 구조한 사람의 전문가에 의존지식 분산, 페어 프로그래밍

Netflix의 카오스 엔지니어링은 안티프래질 설계의 대표적 사례다. Netflix는 Chaos Monkey를 통해 프로덕션 환경에서 의도적으로 서비스를 중단시킨다. 이 "스트레스"가 시스템을 더 견고하게 만든다. 장애를 피하려 하지 않고, 장애를 활용하여 시스템을 강화하는 것이다.


9장: 집단 지성과 개인 지식의 한계

사회적 인식론의 관점

전통적 인식론은 개인의 지식에 초점을 맞추었지만, **사회적 인식론(Social Epistemology)**은 지식의 사회적 차원에 주목한다. 지식은 개인의 머릿속에만 존재하는 것이 아니라, 집단의 상호작용을 통해 생성되고 검증된다.

소프트웨어 엔지니어링에서 이 관점은 매우 중요하다. 현대의 소프트웨어 시스템은 한 사람이 전체를 이해하기에는 너무 복잡하다. 그래서 우리는 집단적 지식 메커니즘에 의존한다.

코드 리뷰의 인식론적 근거

코드 리뷰는 단순한 품질 관리 절차가 아니다. 인식론적으로 볼 때, 코드 리뷰는 다중 인식 주체에 의한 지식 검증 과정이다.

한 사람이 코드를 작성할 때, 그 코드에 대한 지식은 개인적이고 주관적이다. 코드 리뷰를 통해 다른 사람의 관점이 더해지면, 지식은 **상호주관적(intersubjective)**이 된다. 이것은 개인적 믿음보다 더 강한 정당화를 제공한다.

코드 리뷰가 제공하는 인식론적 가치:

가치설명인식론적 의미
다관점 검증다른 배경의 엔지니어가 동일 코드를 검토확증 편향 감소
암묵적 지식 공유리뷰 과정에서 맥락과 경험이 공유됨집단 지식 증가
가정 노출작성자가 당연시한 가정을 리뷰어가 질문숨겨진 전제 검증
지식 분산코드 이해가 한 사람에서 팀으로 확산단일 장애점 제거

ADR과 RFC의 인식론적 역할

**Architecture Decision Records(ADR)**와 Request for Comments(RFC) 프로세스는 의사결정의 인식론적 품질을 높이는 메커니즘이다.

ADR의 인식론적 기능:

  1. 명시적 정당화: "왜 이 결정을 했는가"를 기록하여, 미래의 자기 자신과 동료에게 정당화의 근거를 제공한다
  2. 맥락 보존: 결정 당시의 제약조건과 배경을 기록하여, 지식이 맥락과 분리되는 것을 방지한다
  3. 대안 기록: 검토했으나 채택하지 않은 대안을 기록하여, 반증 시도의 흔적을 남긴다
  4. 가역성 확보: 결정이 잘못되었을 때 되돌리기 위한 근거를 마련한다

RFC 프로세스의 인식론적 기능:

  1. 사전 비판: 구현 전에 설계를 집단적으로 검증하여, 비용이 낮은 단계에서 오류를 발견한다
  2. 다양성 확보: 다양한 배경의 의견을 수집하여 편향을 줄인다
  3. 증거 기반 논의: 주장에는 근거를 요구하여, 단순한 의견이 아닌 정당화된 주장을 촉진한다

10장: 실천적 함의 - 인식론적 겸손을 엔지니어링 문화에 녹이기

"모른다"고 말할 수 있는 문화 만들기

조직 문화에서 "모른다"가 안전한 발언이 되려면, 구조적 장치가 필요하다.

1. 리더가 먼저 모범을 보인다

기술 리더나 매니저가 "저도 이 부분은 확실하지 않습니다"라고 말할 때, 팀 전체의 심리적 안전감이 올라간다. Google의 Project Aristotle 연구는 **심리적 안전감(Psychological Safety)**이 고성과 팀의 가장 중요한 요소임을 밝혔다.

2. 불확실성을 기록하는 관행을 만든다

  • 기술 문서에 "Confidence Level" 표시: 확실한 내용은 HIGH, 가정에 기반한 내용은 MEDIUM, 추측은 LOW
  • ADR에 "우리가 모르는 것(Unknown Unknowns)" 섹션 추가
  • 코드 주석에 불확실한 부분 표시: "이 최적화가 실제로 효과가 있는지는 벤치마크로 확인 필요"

3. 질문을 장려하는 메커니즘을 만든다

  • "멍청한 질문은 없다"를 넘어서, "좋은 질문에 대한 보상" 시스템
  • 코드 리뷰에서 "왜?"라는 질문을 장려하고, 질문에 성실히 답하는 문화
  • 온보딩 과정에서 "질문 목록"을 의무적으로 작성하게 하기

가설-검증 사이클로서의 스프린트

애자일 스프린트를 포퍼의 반증주의 관점에서 재해석할 수 있다.

스프린트 단계반증주의적 해석
스프린트 계획가설 수립: "이 기능을 이렇게 구현하면 사용자가 만족할 것이다"
개발가설 구체화: 코드라는 형태로 가설을 명확히 한다
테스트반증 시도: 가설이 틀릴 수 있는 시나리오를 탐색한다
배포실험 실행: 가설을 현실 세계에 노출시킨다
리뷰/회고결과 분석: 가설이 반증되었는가, 아니면 잠정적으로 수용되는가?

불확실성을 인정하는 기술 문서 작성

기술 문서에서 불확실성을 다루는 프레임워크를 제시한다.

확신도 표기 시스템:

표기의미사용 맥락
CONFIRMED테스트, 증명, 또는 공식 문서에 의해 확인됨검증된 사실 기술 시
EXPECTED높은 확신이지만 직접 검증하지는 않음문서 기반 추론 시
ESTIMATED경험과 직관에 기반한 추정성능, 용량 추정 시
ASSUMED검증되지 않은 가정, 추후 확인 필요가정 명시 시
UNKNOWN알 수 없음, 추가 조사 필요미지의 영역 표시 시

이 표기를 기술 문서에 일관되게 적용하면, 독자는 각 정보의 확실성 수준을 즉시 파악할 수 있다.


11장: 체크리스트 - 인식론적 겸손을 실천하는 엔지니어의 습관

일일 실천 습관

  • 코드를 작성할 때 "이 코드가 실패할 수 있는 경우"를 먼저 생각한다
  • 코드 리뷰에서 최소 하나의 "왜?" 질문을 던진다
  • "확실합니다"라는 말 대신 "현재까지의 정보로는 이렇게 판단합니다"라고 말한다
  • 하루에 한 번, "내가 오늘 모른다고 인정한 것"을 되돌아본다

주간 실천 습관

  • 팀 미팅에서 "이번 주에 우리가 틀렸던 가정"을 공유한다
  • 기술 문서에 확신도 표기를 업데이트한다
  • 모니터링 대시보드에서 "예상과 다른 패턴"을 찾아본다
  • "우리가 모르는 것" 목록을 검토하고 업데이트한다

의사결정 시 체크리스트

  • 이 결정의 근거가 되는 데이터는 무엇인가?
  • 이 데이터가 틀릴 수 있는 경우는 무엇인가?
  • 반대 의견을 가진 사람의 관점에서 이 결정을 바라보았는가?
  • 이 결정이 틀렸을 때 되돌릴 수 있는가? (가역적 vs 비가역적)
  • 이 결정에 영향을 미칠 수 있는 나의 편향은 무엇인가?
  • "모른다"고 답해야 할 부분은 없는가?

코드 리뷰 시 체크리스트

  • 코드를 읽기 전에 PR 설명에 의해 판단이 고정되지 않았는가? (앵커링 검증)
  • 핵심 로직을 직접 머릿속에서 실행해보았는가? (수동 트레이싱)
  • "이 코드가 실패하려면 어떤 입력이 필요한가?"를 생각했는가? (반증적 사고)
  • 테스트가 의미 있는 것을 검증하고 있는가? (테스트 품질 검증)
  • "작성자가 나보다 이 코드를 잘 알 것"이라는 가정에 빠지지 않았는가? (권위 편향 검증)
  • 내가 이 코드를 정말 이해했는가, 아니면 이해했다고 느끼는 것인가? (이해의 착각 검증)

장애 대응 시 체크리스트

  • 첫 번째 가설에 고착되지 않았는가? (앵커링 효과)
  • 증거에 따라 원인 확률을 업데이트하고 있는가? (베이지안 사고)
  • "이 원인이 아닐 수도 있다"는 가능성을 열어두고 있는가? (반증적 사고)
  • 과거의 유사 장애가 현재 판단을 왜곡하고 있지 않는가? (가용성 편향)
  • 팀원의 다양한 관점을 수렴하고 있는가? (사회적 인식론)

결론: 소크라테스의 지혜, 엔지니어의 겸손

2,500년 전 소크라테스의 "나는 내가 모른다는 것을 안다"는 고백은, 오늘날 소프트웨어 엔지니어링에서 그 어느 때보다 절실한 지혜다.

우리가 만드는 시스템은 점점 더 복잡해지고, 상호 연결되며, 예측 불가능해지고 있다. 이 복잡성 앞에서 "완전히 이해했다"는 확신은 가장 위험한 형태의 무지다.

인식론이 소프트웨어 엔지니어링에 주는 핵심 교훈은 다음과 같다.

  1. 지식은 절대적이지 않다: 코드의 정확성은 증명이 아닌 반증의 부재로만 주장할 수 있다 (포퍼)
  2. 확신은 편향을 숨긴다: 우리의 판단은 다양한 인지 편향에 의해 왜곡된다 (Kahneman)
  3. 불확실성은 적이 아니라 정보다: 불확실성을 활용하면 시스템은 더 강해진다 (탈레브)
  4. 집단의 지혜는 개인의 확신보다 강력하다: 코드 리뷰, ADR, RFC는 사치가 아니라 인식론적 필수품이다

Gerald Weinberg가 50년 전에 말했듯이, 프로그래밍은 궁극적으로 심리적 활동이다. 그리고 인식론적 겸손은 더 나은 소프트웨어를 만드는 가장 근본적인 심리적 자질이다.

"진정한 지식은 자신의 무지를 아는 것이다." - 소크라테스

기술적 겸손은 약점이 아니다. 그것은 복잡성과 불확실성의 세계에서 살아남기 위한 가장 강력한 엔지니어링 원칙이다.


참고 자료

  1. Karl Popper, The Logic of Scientific Discovery (1934) - 반증주의의 기본 원리와 과학적 방법론
  2. Edsger Dijkstra, "On the Cruelty of Really Teaching Computing Science" (1988) - 소프트웨어 테스팅의 본질적 한계
  3. Daniel Kahneman, Thinking, Fast and Slow (2011) - 인지 편향과 의사결정의 심리학
  4. Nassim Taleb, Antifragile: Things That Gain from Disorder (2012) - 불확실성을 활용하는 시스템 설계
  5. David Dunning and Justin Kruger, "Unskilled and Unaware of It" (1999) - 더닝-크루거 효과의 원 논문
  6. Gerald Weinberg, The Psychology of Computer Programming (1971) - 프로그래밍의 심리적 측면
  7. Nassim Taleb, The Black Swan (2007) - 극단적 불확실성과 예측의 한계
  8. Google Research, "Project Aristotle" - 고성과 팀의 핵심 요소로서의 심리적 안전감