실용주의 편집증
- 4.1 계약에 의한 설계
- 4.2 죽은 프로그램은 거짓말을 하지 않는다
- 4.3 단정적 프로그래밍
- 4.4 리소스 사용의 균형
- 4.5 헤드라이트를 앞서가지 말라
핵심 전제는 이거야: 자기 코드를 포함해서, 어떤 코드도 완벽하지 않아. 실용주의 프로그래머는 자기 자신도 믿지 않거든. 그래서 방어적으로 코딩하지.
버트런드 마이어의 Eiffel 언어에서 가져온 개념이야. 소프트웨어 모듈이 서로 상호작용할 때 계약을 맺으라는 거지.
계약의 세 요소:
- 사전 조건(Precondition) — 함수를 호출하기 전에 호출자가 보장해야 하는 것. "이 값은 null이 아니어야 한다"
- 사후 조건(Postcondition) — 함수가 끝난 후에 함수가 보장하는 것. "반환값은 항상 양수다"
- 클래스 불변식(Class Invariant) — 클래스의 생명 주기 동안 항상 참인 것
계약이 왜 좋냐면:
- 버그를 발생 지점에서 바로 잡을 수 있어. 잘못된 입력이 시스템 깊숙이 들어가서 엉뚱한 곳에서 터지는 걸 방지하지
- 문서 역할도 해. 계약 자체가 "이 함수는 뭘 기대하고 뭘 보장하는가"를 명시하거든
대부분의 주류 언어에는 Eiffel 같은 네이티브 DBC 지원이 없어. 그래서 실무에서는 assert, 타입 시스템, 입력 검증으로 대체하지. 중요한 건 형식이 아니라 **"모듈 간 기대를 명시적으로 만든다"**는 사고방식이야.
일찍 작동을 멈춰라(Crash Early).
에러가 발생했는데 "이건 일어나면 안 되는 건데..." 하면서 계속 실행시키면 어떻게 될까? 잘못된 데이터로 계속 처리하다가 더 큰 재앙이 되지. 잘못된 데이터를 DB에 쓰고, 그 데이터로 다른 계산을 하고, 고객한테 잘못된 결과를 보내고.
차라리 빨리 죽는 게 나아. 죽은 프로그램은 최소한 거짓말은 안 하거든.
저자들의 조언:
- 예외를 삼키지 마.
catch (Exception e) { // 무시 }이런 코드가 가장 위험해 - "불가능한" 상황이 발생하면 프로그램을 멈춰
- 에러 핸들링에서 중요한 건 **"복구할 수 있는가?"**야. 복구할 수 없으면 빨리 실패해
Erlang의 "Let it crash" 철학이 이 원칙의 극단적 형태지. 프로세스가 죽으면 슈퍼바이저가 새로 살리는 구조야. 개별 프로세스가 방어적으로 버티는 것보다 깔끔하게 죽고 다시 시작하는 게 더 안정적이라는 거야.
"이런 일은 절대 일어나지 않을 거야" — 이 말이 나오면 assert를 넣어.
assert는 "이 조건이 참이 아니면 프로그램을 멈춰라"는 명령이야. 방어적 프로그래밍의 핵심 도구지.
assert(result != null, "결과가 null이면 안 됨");
assert(count >= 0, "카운트가 음수가 될 수 없음");
흔한 실수: 프로덕션에서 assert를 꺼. 저자들은 이걸 반대하거든. "방탄조끼를 입고 연습하다가, 실전에 나가면서 벗는 것과 같다"는 비유야. assert가 성능에 영향을 준다면 그 assert만 선별적으로 빼지, 전부 끄는 건 위험해.
assert와 에러 핸들링은 달라:
- assert: 절대 일어나면 안 되는 것. 프로그래머의 실수를 잡기 위한 것
- 에러 핸들링: 일어날 수 있는 것. 사용자 입력 오류, 네트워크 실패 등
시작한 것은 끝내라(Finish What You Start).
리소스를 할당한 코드가 해제도 책임져야 해. 파일을 열었으면 닫아야 하고, 메모리를 할당했으면 해제해야 하고, 트랜잭션을 시작했으면 커밋이든 롤백이든 끝내야 하지.
언어별 패턴:
- C/C++: RAII (Resource Acquisition Is Initialization)
- Java: try-with-resources
- Python: with 문
- Go: defer
중첩된 리소스의 경우: 할당의 역순으로 해제해. 먼저 연 걸 나중에 닫는 거야. 스택처럼.
여러 곳에서 같은 리소스를 쓰는 경우: 소유자를 명확히 해. 누가 할당하고 누가 해제하는지 애매하면 반드시 리소스 누수가 생기거든.
20주년 기념판에서 새로 추가된 절이야. 너무 먼 미래를 예측하려 들지 마.
밤에 운전할 때 헤드라이트가 비추는 만큼만 볼 수 있잖아. 그 이상을 보려고 속도를 높이면 사고가 나지. 소프트웨어도 마찬가지야.
구체적으로:
- 확실하지 않은 미래 요구사항을 위해 코드를 복잡하게 만들지 마
- "나중에 필요할지도 모르니까" 같은 이유로 추상화 레이어를 추가하지 마
- 한두 단계 앞까지만 생각해. 그 이상은 어차피 틀리거든
이 절은 YAGNI(You Ain't Gonna Need It) 원칙과 통하면서도, 2장의 가역성과도 연결돼. 미래를 예측하는 대신, 미래에 바꿀 수 있게 만드는 데 집중하라는 거야.
정리
4장 읽고 기억할 거 세 가지:
- 계약을 명시해. 함수가 뭘 기대하고 뭘 보장하는지 코드로 표현해야 하지
- 빨리 실패해. 잘못된 상태로 계속 달리는 것보다 멈추는 게 나아
- 헤드라이트 너머를 보려 하지 마. 확실한 범위 안에서 작은 단계로 나아가는 거야