한 번 vs 두 번의 차이

같은 종류 실수가 반복되는 패턴이 있다.

  • frontmatter YAML에서 값이 따옴표로 시작 → 빌드 실패. 고침. 몇 주 후 다른 파일에서 또 같은 실수.
  • PR에 테스트 빼먹음. 리뷰에서 지적. 다음 PR에서 또.
  • 배포 전 환경 변수 확인 누락. 장애. 다음 배포에서 또.

첫 번째는 그냥 실수다. 고치면 된다. 두 번째부터는 다르다. 같은 실수가 반복된다는 건 개인의 부주의가 아니라 그걸 막는 시스템이 없다는 신호다. 사람 기억에 의존하는 한 세 번째도 온다.

대응 격상 사다리

재발 횟수에 따라 대응을 격상한다.

1회 — 수정 (fix)

그냥 고친다. 기록도 선택. 누구나 실수한다.

2회 — 체크리스트 / 자동 검사

같은 함정이 두 번이면 사람 기억을 신뢰하지 않기로 결정한다.

  • 체크리스트 항목 추가 (PR 템플릿·배포 절차)
  • 가능하면 자동 검사로: lint 룰, pre-commit hook, CI 검증
# YAML 따옴표 함정이 2번 → 빌드 전 자동 점검
grep -rnE '^(title|summary): "' src/content/ && echo "FAIL: 따옴표로 시작하는 frontmatter"

체크리스트보다 자동 검사가 낫다. 체크리스트도 사람이 건너뛸 수 있으니까. 기계가 강제할 수 있으면 기계로.

3회 — 프로세스 실패로 취급

두 번에서 체크리스트/자동화를 했는데도 세 번째가 왔다면, 그 대응이 작동 안 한 것. 더 강한 강제가 필요하다.

  • 검사를 CI 차단(blocking)으로 격상
  • 또는 그 실수가 불가능한 구조로 재설계 (예: 따옴표 문제면 frontmatter 생성을 스크립트로 자동화)

3회는 "더 조심하자"가 아니라 "구조를 바꾸자"다.

왜 2회가 임계인가

1회에 자동화하면 과잉이다. 한 번 일어난 모든 실수에 hook을 달면 hook이 노이즈가 된다(좋은 알람의 신호/노이즈 문제와 같다).

3회까지 기다리면 비용이 누적된다. 이미 세 번 당했다.

2회가 "우연 아님"이 확인되는 가장 빠른 지점이다. 우연한 실수는 보통 한 번. 두 번 같은 형태면 패턴이다. rule of three가 아니라 콘텐츠/운영에선 rule of two가 적절할 때가 많다 — 재발 비용이 클수록 임계를 낮춘다.

무엇을 자동화 대상으로

모든 실수가 자동 검사 가능하진 않다. 격상 가능 여부로 분류.

함정 유형자동화 가능?대응
문법·형식 (YAML·lint)✅ 쉬움grep / linter / hook
누락 (테스트·env)✅ 중간CI 검사 / 템플릿 필수 항목
논리·판단 오류❌ 어려움체크리스트 + 리뷰
설계 결정❌ 어려움페르소나·문서화

자동화 안 되는 것은 체크리스트 + 리뷰로. 자동화 가능한 것을 체크리스트로만 두면 결국 또 빠진다.

함정

  • 재발인데 또 수정만: "이번에도 조심하면 되지"가 세 번째를 부른다. 2회면 자동화 결정.
  • 1회에 과잉 자동화: 모든 실수에 hook → hook 노이즈. 2회 임계 유지.
  • 체크리스트가 너무 길어짐: 30항목 체크리스트는 아무도 안 본다. 자동화 가능한 건 자동으로 옮기고 체크리스트는 짧게.
  • 자동 검사를 non-blocking으로: 경고만 하고 안 막으면 결국 무시된다. 재발 비용 크면 blocking.
  • 검사 자체가 stale: 시스템 바뀌면 검사도 갱신. 안 그러면 false negative.
  • 자산화 안 함: 재발 함정과 그 대응을 기록 안 하면 팀의 다른 사람이 또 빠진다. debugging 노트/runbook로.

핵심

한 번은 실수, 두 번은 시스템의 공백이다. 재발 횟수에 따라 수정 → 자동 검사 → 구조 재설계로 격상한다. 사람 기억을 신뢰하는 한 세 번째는 온다.

관련

/notes/deterministic-loop — 자동 검사를 결정론 루프로 /logs#2026-06-09-ci-gate-ratchet — 검사를 non-blocking에서 blocking으로 점진 강화하는 구체 운영