Rob Pike의 프로그래밍 5가지 규칙 (1989)
(cs.unc.edu)- 규칙 1: 프로그램이 어디서 시간을 소비할지 예측할 수 없음. 병목은 예상치 못한 곳에서 발생하므로, 실제로 병목임이 입증되기 전까지 속도 개선 시도 금지
- 규칙 2: 측정이 우선. 속도 조정은 측정 후에만 수행하며, 코드의 한 부분이 전체를 압도할 때만 최적화 고려
- 규칙 3: 복잡한 알고리듬은 작은 n에서 느림. 복잡한 알고리듬은 큰 상수를 가지며, n이 자주 커지지 않는 한 단순한 방법 사용. n이 커지더라도 먼저 규칙 2를 적용해야 함
- 규칙 4: 복잡한 알고리듬은 버그가 많고 구현이 어려움. 단순한 알고리듬과 단순한 데이터 구조를 사용하는 것이 바람직함
- 규칙 5: 데이터가 핵심. 올바른 데이터 구조를 선택하고 잘 조직하면, 알고리듬은 거의 자명하게 드러남. 프로그래밍의 중심은 알고리듬이 아니라 데이터 구조임
관련 철학 및 인용
- 규칙 1과 2는 Tony Hoare의 “조기 최적화는 만악의 근원” 이라는 격언과 동일한 의미
- Ken Thompson은 규칙 3과 4를 “의심스러울 땐 단순한 방법(Brute Force)을 써라” 로 재해석
- 규칙 3과 4는 KISS(Keep It Simple, Stupid) 설계 철학의 예시
- 규칙 5는 Fred Brooks가 『The Mythical Man-Month』에서 이미 언급한 내용으로,
종종 “똑똑한 객체를 사용하는 단순한 코드 작성” 으로 요약됨
Hacker News 의견들
-
Jonathan Blow의 강연을 떠올리게 됨
그는 생산성 관점에서 접근했음. Braid 개발 초기에는 거의 모든 걸 단순한 배열로 구현했고, 병목이 생길 때만 수정했음
“속도나 메모리보다 더 중요한 건, 프로그램 하나를 구현하는 데 걸리는 인생의 시간”이라는 말이 인상적이었음- 게임은 수많은 유사 객체를 초당 60프레임 이상으로 반복 처리하므로, 단순한 배열 기반 구조가 합리적인 기본값임
- 게임 개발에서는 16ms 안에 프레임을 렌더링해야 하므로, 고정 크기 테이블과 선형 탐색이 흔한 패턴임. 동적 할당의 예측 불가능성을 피하기 위한 구조적 선택임
- 이 관점은 게임 외의 일반 개발에도 통함. 우리 모두는 마감과 비용 제약 속에서 일하므로, 엔지니어링 시간 자체가 비용임
- 다만 게임의 교훈을 일반 프로그래밍에 그대로 확장하는 건 조심해야 함. 게임은 재사용보다 특수 목적 코드 중심이고, 일반 소프트웨어는 데이터 중심임
- 나의 경우는 배열 대신 해시맵으로 시작하는 타입임
-
Rule 1을 진심으로 받아들이면, Rule 3~5는 자연스럽게 따라옴
병목을 예측할 수 없다는 전제를 인정하면, 단순한 코드 작성과 측정이 유일한 합리적 전략이 됨
실제로 가장 자주 실패하는 건 조기 최적화가 아니라 조기 추상화임. 필요하지 않은 유연성을 위해 복잡한 계층을 만들고, 그게 오히려 유지보수 비용을 키움- “추상화는 자연스럽게 등장해야지, 미리 설계하는 게 아니다”라는 말을 팀 내에서 자주 인용함
- 조기 추상화는 개발자 시간을 낭비시키고, 기술 부채를 늘리며, 버그 가능성을 높임. 종종 ‘미래의 문제를 대비한다’는 명목으로 생김
- 관련 논문으로 Philip Wadler의 글을 참고할 만함
- 나는 성능보다 가독성과 유지보수성을 더 중시함. 그래서 나에겐 Rule 4가 근본이고, Rule 1은 그 결과임
- 복잡한 설정 파일을 과도하게 분리한 사례를 겪었음. 결국 단순한 YAML 8개 파일로 충분했음
-
90년대 새벽 2시에 데이터셋 검색 기능을 구현해야 했던 경험이 있음
피곤해서 일단 선형 탐색으로 구현하고 나중에 고치기로 했는데, 실제로는 4시간짜리 테스트 중 6초 차이밖에 안 났음
결국 수정은 했지만, 의미 있는 차이는 없었음- 작은 n에서는 오히려 선형 탐색이 더 빠를 수도 있음
- 하지만 O(n²) 알고리즘은 “배포는 되지만, 결국 운영 중 폭발하는” 위험이 있음
-
Rule 5에 전적으로 공감함
어려운 문제일수록 데이터 구조와 API의 반복적 개선으로 해결됨. 구조가 잘 잡히면 제어 흐름은 자연스러워짐
LLM은 이런 구조적 사고에는 약함. 복잡한 제어 흐름은 잘 제안하지만, 조합 가능한 데이터 구조 설계는 잘 못함- 내 경험상 Rule 5가 사실상 Rule 1임. “코드를 보여주면 혼란스럽지만, 데이터베이스 스키마를 보여주면 모든 게 명확해진다”는 말이 있음
- 분산 서비스에서는 Rule 5가 자주 무시됨. 여러 HTTP·DB 호출을 나누는 대신, 한 번의 호출로 처리할 수 있는 구조가 더 효율적임
-
나는 전자공학(E.E.) 출신으로, Rule 3 덕분에 커리어 초반엔 큰 문제 없었음
하지만 나중에 대규모 시스템으로 옮기면서 n이 커지고, 복잡도가 실제로 중요해졌음
Rob Pike가 말하던 시대의 ‘빅 아이언’은 지금의 임베디드 환경과 비슷했음- 나는 Rule 3에 일부 반대함. 작은 입력에서는 상관없지만, 큰 입력에서는 빅오 성능이 중요함.
두 알고리즘이 구현 난이도가 비슷하다면, 큰 입력에서 빠른 쪽을 택함
관련 글: Less Than Quadratic - “fancy”의 의미를 문제 도메인에 맞게 해석해야 함. n이 작으면 단순한 접근이 낫지만, n이 크면 고급 알고리즘이 필수임
종종 사람들은 너무 단순한 O(n²) 접근을 택해 운영 중 폭발을 경험함 - 아버지는 Fortran 시절부터 lookup table을 즐겨 썼음. 반복 계산을 줄이는 고전적 최적화 방식임
- 나는 Rule 3에 일부 반대함. 작은 입력에서는 상관없지만, 큰 입력에서는 빅오 성능이 중요함.
-
Pike의 규칙들이 기존 격언보다 낫다고 생각함
“조기 최적화는 만악의 근원” 같은 문장은 맥락이 사라져 오해되기 쉬움
Pike의 버전은 명확하고 오용하기 어렵음
“Rule 5는 ‘멍청한 코드가 똑똑한 객체를 사용하게 하라’로 요약된다”는 옛 표현이 있었는데,
객체지향 시대에는 오히려 복잡성을 숨기는 잘못된 신념으로 변질되었음- 역사적 사고의 연결 고리를 유지하는 건 중요함
- “고전 격언의 정신적 클릭베이트”라는 표현은 일종의 재치 있는 비유로 느껴짐
-
10년 넘게 같은 코드베이스를 운영하면서 Pike의 규칙을 완전히 체화했음
KISS/DRY 원칙을 지키며, 유행 기술보다 검증된 기술을 유지하는 게 장기적으로 안정적이었음
실제로 우리 팀의 개발 원칙 문서는 Pike의 규칙과 거의 동일한 내용임 -
1990년대 초 C++ 시절, 이중 연결 리스트 대신 단순 배열 재할당으로 바꿨더니
버그가 사라졌을 뿐 아니라 오히려 더 빨라졌음.
비싼 연산을 덜 자주 수행하는 게 좋은 전략임을 배웠음 -
“측정 없이 튜닝하지 말라”는 규칙과 Jeff Dean의 Latency Numbers Every Programmer Should Know를 비교하면 흥미로움
Dean은 사전 지식으로 성능을 예측할 수 있다고 말함
결국 두 입장은 조화될 수 있음 — 설계 단계에서는 감각적으로 빠른 구조를 택하고, 구현 후에는 측정 기반으로 미세 조정해야 함- 진짜 빠른 코드는 두 접근을 모두 사용함. 설계 단계에서 캐시 효율성을 고려하고, 이후 프로파일링으로 병목을 제거함
- 지연(latency) 수치는 알고리즘의 이론적 한계선을 알려줄 뿐, 실제 성능은 구현과 런타임 환경에 따라 달라짐
- ‘조기 최적화’가 금지하는 건 국소적 해킹 수준의 튜닝임. 전체 설계에서 속도를 고려하는 건 당연함
-
Rule 1과 2는 새로운 문제를 다룰 때만 절대적임
같은 도메인에서 반복적으로 시스템을 만들다 보면, 병목 지점을 미리 예측할 수 있음
경험 많은 개발자라면 설계 전부터 대략적인 성능 감을 가질 수 있음- 나도 대부분의 경우 병목을 정확히 예측했음. 사전 성능 테스트로 접근 방식을 바꾼 적도 많음
- 하지만 30년간의 경험상, 병목이 생길 것 같은 감은 맞지만, 정확한 위치나 시점은 예측 불가임
- “Rob Pike가 뭘 알겠냐”는 농담도 있었지만, 그는 Unix와 Go를 만든 사람임. 그의 규칙엔 이유가 있음