Chapter 08 - 스타일 가이드와 규칙
- 대부분의 엔지니어링 조직에는 내부 코드베이스를 관리하는 규칙이 있고, 구글에도 역시 존재한다. 규칙은 지켜야만 하는 것이고, 지침은 권장과 모범 사례다. 구글에서는 규칙을 모아서 '프로그래밍 스타일 가이드'를 만들었다. 언어별로 스타일 가이드를 관리하고 있으며, 목표는 '지속 가능성'을 높이는 것이다. 스타일 가이드가 담아야 하는 내용에 대해 정해진 규칙은 없다.
8.1 규칙이 필요한 이유
- 규칙이 필요한 이유는 조직이 추구하는 '좋은' 가치를 추구하는 행동을 장려하기 위해서다. '나쁜' 행동을 억제하기도 한다. 규칙이 있으면 무의식적으로 '좋은' 코드를 작성한다.
8.2 규칙 만들기
- 규칙을 만들기 전 '어떤 목표를 이루려 하는가?'를 생각해야 한다.
8.2.1 기본 원칙 안내
- 구글은 3만명의 엔지니어가 있고 20억 라인 이상의 코드베이스에 매일 약 6만 건의 코드가 서브밋된다. 구글의 스타일 가이드는 '규모와 시간 양쪽 측면에서 탄력적인 엔지니어링 환경이 지속되도록' 하기 위해서 존재한다. 규칙에는 자유와 유연성을 희생하는 트레이드오프가 발생하지만 일관성을 높이고 의견 대립을 줄이므로 혜택이 더 크다. 규칙을 만들 때 중요한 원칙은 다음과 같다.
- 규칙의 양을 최소화합니다.
- 코드를 읽는 사람에게 맞춥니다.
- 일관되어야 합니다.
- 오류가 나기 쉽거나 예상치 못한 동작을 유발하는 구조를 피합니다.
- 꼭 필요하다면 실용성을 생각해 예외를 허용합니다.
규칙의 양을 최소화한다
- 규칙이 많아지면 관리가 어렵고 비용도 커진다. 규칙을 적게 유지해야한다. 너무 자명한 규칙은 의도적으로 배제한다. C++에서 goto문을 사용하지 않는 규칙은 이미 당연시되어 있어 배재했다.
읽는 사람에게 맞춘다
- 읽기에 간단한 코드에 가치를 두기로 했다. 코드는 작성되는 횟수보다 읽히는 횟수가 더 많고 시간이 지날수록 차이가 더 벌어진다. 이 독자 중심주의의 일환으로 코드에 의도를 알려주는 증거를 남게해야한다. 행위가 헷갈리거나 오해할 수 있는 상황에서 근거가 더욱 중요해진다. 주석도 같은 목표를 지향한다. 그래서 주석은 문서화 주석(파일, 클래스, 함수의 앞에 추가되는 블록 주석), 구현 주석(코드 자체에 산재하는 주석)으로 분류한다.
읽는 사람에게 맞추지 않는 코드를 짠 경우가 많았던 것 같다.
일관되어야 한다
- 구글은 출입카드, 와이파이 등 코드 외적으로도 일관성을 유지한다. 사소한 설정에 낭비하는 시간을 줄이고 장소를 옮겨도 업무가 쉽다. 이런 규칙은 코드에도 똑같이 적용된다.
일관성이 안겨주는 이점
일관성의 가치는 크다. 엔지니어와 읽는 이들이 '어떻게'를 표현하느냐가 아니라 '무엇을' 수행하느냐에 집중할 수 있게 된다. 규모의 확장에도 도움된다. 규모의 확장은 도구의 도움이 필수인데, 일관된 코드는 도구를 적용하기가 더 쉽다. 인력 재배치에도 좋다. 시간 관점에서도 좋다. 시간이 흐르며 인력이 재배치되고 코드를 관리하는 팀이 바뀌기도 한다.
*하지만 전체 언어의 코드베이스에 일관성을 지켜야한다는 것은 아니다. 규모가 커지면 완벽한 일관성을 추구하기에는 얻는 것보다 비용이 큰 단계에도 도달한다.
표준 정하기
- 구글은 일관성 중에서도 대체로 사내에서의 일관성을 더 중요하게 생각한다. 다만 길게 보면 외부의 표준을 따르는게 더 좋다. 외부 코드와 상호작용하고 심지어 바깥세상에 나가 생을 마감할 수도 있다.
오류를 내기 쉽거나 예상과 다르게 동작할 여지가 있는 구조는 피하자
- 복잡한 기능에는 언뜻 봐서는 지나칠 수 있는 미묘한 함정이 숨어 있는 경우가 많다. 이런 이유로 고급 기능을 사용하지 못하게 제한한다. 파이썬의 경우 리플렉션 등. 해당 언어의 전문가가 아닌 신뢰성 엔지니어(SRE)가 작업을 할 수도 있기 때문이다.
실용적 측면을 인정하자
- 무조건적인 것이 아니라 꼭 필요하다면 최적화나 실용성을 위해 예외를 허용한다. 일관성과 가독성을 희생해서라도 성능을 끌어올려야 할 때가 있다. 상호운용성도 중요하다. 외부 코드와 연동되거나 OS의 기능을 사용해야하는 경우. 일관성은 매우 중요하지만 융통성이 없어서는 안된다.
8.2.2 스타일 가이드
- 스타일 가이드의 규칙은 세 범주로 나눌 수 있다.
- 위험을 피하기 위한 규칙
- 모범 사례를 적용하기 위한 규칙
- 일관성을 보장하기 위한 규칙
스타일 가이드가 담아야하는 내용에 대한 규칙은 없다고 했는데, 이 세 범주가 그런 내용을 담고 있는 것 같다. 꼭 이런 내용을 담으라는게 아니라 결국 세 가지 범주로 나눠볼 수 있다는 말일까?
위험 회피하기
- 기술적인 이유 때문에 반드시 써야 하거나 쓰면 안 되는 언어 특성들에 관한 규칙들이 담겨 있습니다. 어떤 언어 특성은 사용하고 어떤 구조는 피해야 하는지를 설명하는 규칙들이다. 어떤 언어 기능은 직관적이지 않거나 올바르게 사용하려면 아주 세심한 데까지 주의해야 해서 미묘한 버그를 유발하기 때문이다.
모범 사례 강제하기
- 다양한 모범 사례도 담아서 건실하고 지속 가능한 코드를 생산하는 습관을 몸에 익히도록 한다. 모범 사례 중 일부는 소스 코드의 가독성을 높이도록 설계되었다. 포맷팅 규칙 상당수가 이 범주에 속한다. 새롭거나 널리 이해되지 못한 언어 기능은 제한한다. std::unique_ptr 도입이 이 경우에 속한다.
일관성 구축하기
- 사소한 문제를 다루는 규칙도 많은데 이런 규칙의 목적은 단순히 결정을 내리고 결정을 문서로 남기는 것이다. 예를들어 들여쓰기 공백 수, 임포트문 순서 같은 것인데, 이는 어떤 선택을 하든 명확하고 가시적인 차이가 없는 경우가 대부분이다. 대신 하나를 선택해서 논쟁에서 벗어나고 더 중요한 일로 시선을 돌릴 수 있게 한다.
속 시원하다. 왜 이런 논쟁은 쉽게 일어나고 열띤 토론이 벌어질까? 쉬운 문제라서 그런 것 같다. 누구나 처음부터 끝까지 본인의 결론을 내리기가 쉽다. 그래서 거의 모든 사람이 주장이 생기기 때문아닐까? 토론이 많이되거나 의견이 다양한 경우 중요하지 않은 문제라고 생각하고 결정권자가 결정을 빨리 내려버리는 것이 해결방법일 것 같다.
그 외...
- 스타일 가이드에 없는 것도 많지만 코드베이스의 건실성에 영향을 크게 주는 규칙들에 집중하려고 노력한다. '바퀴를 다시 발명하지 말자', '너무 똑똑하게 짜지 말자'와 같은 규칙은 스타일 가이드에 포함하지 않는다.
8.3 규칙 수정하기
스타일 가이드는 고정불변이 아니다. 세월이 흐르면 결정에 영향을 준 요인들도 변할 수 있다. 규칙을 유용하고 최신 상태로 유지하려면 업데이트가 필요한 규칙이 무엇인지를 적시에 알아챌 수 있어야 한다. 그래서 구글 스타일 가이드의 규칙에는 각각의 결정을 뒷받침하는 근거를 명시해뒀다. 근거를 남기면 규칙을 다시 평가하거나 수정하기가 쉬워진다.
*카멜 케이스 명명법 사례: C++에서는 카멜 케이스였고 파이썬에서는 스네이크 케이스였다. 그래서 파이썬 스타일 가이드를 처음 정의할 때는 카멜 케이스를 사용했지만 시간이 지나며 외부 파이썬 프로젝트와 연동되는 빈도가 더 많아졌고 비용과 이점을 놓고 토론 끝에 스네이크 케이스로 바꿨다.
8.3.1 프로세스
- 변경을 찾고 갱신하는 프로세스를 수립했다. '해법'을 중심으로 돌아간다. 수정 제안 형식에서는 문제를 찾으면 다른 '해법'을 제시해야한다. 규칙이 수정되어야 하는 시점은 스타일 가이드에 입각해 코드를 작성하는 엔지니어들의 커뮤니티가 가장 유리하다. 수정 제안이 생기면 토론이 시작되고 받아들여지던지 거부당한다. 받아들여지면 최종 승인 단계로 넘어간다
8.3.2 스타일 중재자
- 언어별로 소유자가 있어 최종 결정과 승인을 책임진다. 수정 여부는 엔지니어링 측면의 트레이드오프를 논의하여 결정한다.
수정이 일어나면 수정에 맞춰 기존 코드를 변경하는 비용도 아주 클 것 같다. 앞서서 도구 얘기를 했듯이, 비즈니스 요구사항뿐만 아니라 이런 도구를 개발하고 적용하고 검증하는 것도 중요한 엔지니어링 조직의 일인 것 같다.
8.3.3 예외
- 일부 규칙은 예외를 허용한다. 예외 허용은 가볍게 이루어지지 않는다. 코드베이스의 무결성이 프로젝트의 일관성보다 중요하다. 규칙을 따르기보다 예외를 인정하는 쪽이 이득이라고 판단될 때만 예외를 허용한다.
- C++에서 매크로는 프로젝트 고유의 접두어를 붙이도록 되어있는데, C++는 매크로가 전역 이름공간에 놓이게 되고 이름 충돌을 피해야 하기 때문이다. 하지만 정말 전역으로 사용되는 유틸리티 매크로는 예외가 허용된다.
8.4 지침(guidance)
- 규칙과 더불어 지침도 존재하고 범위도 다양하다. 지침은 주로 사람들이 자주 실수하는 것 혹은 아직 익숙하지 않은 새로운 주제라서 혼란스러워하는 것들에 집중한다. 규칙은 되도록(should) 따라야 하는 것이다. 지침의 예로 입문서(primer)가 있다. 입문서는 가이드가 권장하는 기능을 자세하게 설명한다. 범위가 넓어서 해당 언어를 처음 접하는 엔지니어가 구글에서 개발하는 데 참고해야 할 거의 모든 주제를 다룬다. 이 조언들은 실질적이고 효과적이다. 또한 이런 내용을 교육하는 다양한 수업도 있다. 입문 지침만 있는 것 말고도 심화 자료도 있다.
8.5 규칙 적용하기
- 규칙을 강제하는 방법으로는 교육과 훈련을 통한 사회적인 방법과 도구를 이용한 기술적인 방법이 있다. 그래서 정규 교육을 운영하고 참고 문서를 업데이트하는 데 자원을 투입하고 코드 리뷰를 중점적으로 활용한다. 규칙이 지켜지는지 확인하는데는 사람보다 되도록 자동화 도구를 활용한다. 도구를 사용하면 실수나 누락을 막을 수 있다. 도구를 활용하면 판단의 영역을 없앨 수 있는 장점도 있다. 조직이 커져도 걱정이 없다. 하지만 도구를 활용하더라도 모든 규칙을 강제로 적용하기 어려운 경우도 있다. 때로는 사람이 판단해야 하는 규칙도 있다. 자바의 스타일 가이드에 '클래스의 멤버와 초기화 블록 나열 순서에는 정답이 없다. 클래스마다 순서가 다를 수 있다'는 규칙도 있다.
- 기술보다 사회적인 문제를 다루는 규칙도 있으며 이 범주에 속하는 규칙 중 많은 수는 자세한 내용을 정확하게 기술하기가 어려워 사람에게 맡겨두는게 나을 수 있다. '변경되는 코드의 크기를 작게 하라'는 규칙은 이 범주에 속한다. 라인 수가 적어도 부수 효과가 많다면 '작다'고 평하기가 어렵다.
8.5.1 오류 검사기
- 규칙들의 상당수는 정적 분석 도구로 강제할 수 있다.
8.5.2 코드 포맷터
- 코드의 형식을 일관되게 관리하기 위해 자동 스타일 검사기와 포맷터를 적극 이용한다. 행렬 포맷팅과 같은 특이 상황을 제외하고는 도구가 잘못 포맷팅하는 일은 거의 없다. 프리서브밋 검사로 포맷터를 반드시 사용하게 한다.
8.6 마치며
- 모든 조직에는, 특히 구글의 엔지니어링 조직처럼 큰 조직이라면 코드베이스의 복잡성을 관리하여 감당할 수 있는 수준으로 유지하는데 규칙이 큰 도움이 된다.
8.7 핵심 정리
- 규칙과 지침의 목표는 시간과 확장 관점에서의 탄력성을 높이는 것이어야 한다.
- 상황이 변하면 규칙도 달라져야 하니 규칙이 만들어진 근거 데이터를 알고 있어야 한다.
- 모든 것을 규칙으로 강제해서는 안된다.
- 일관성이 핵심이다.
- 가능한 한 규칙들이 자동으로 적용되도록 해야한다.
느낀 점
- 조직에 맞게 규칙을 만드는 것이 중요하다는 생각이 들었다. 구글이 이렇게 하니까 라는게 아니라. 구글은 3만명의 엔지니어가 있고 20억 라인 이상의 코드베이스에 매일 약 6만 건의 코드가 서브밋된다. 등의 상황과 팀의 변경 신규 인력 채용 등의 필요성과 그에 따른 근거가 있기 때문에 '지속 가능성'을 높이고 '규모와 시간 양쪽 측면에서 탄력적인 엔지니어링 환경이 지속되도록' 목표하는 규칙을 만든 것이다. 우리 회사는?
- '읽기 쉬운 코드', '오류를 내기 쉽거나 예상과 다르게 동작할 여지가 있는 구조는 피하자'는 규칙 등은 조직의 특성이나 규모와 상관없이 적용할 수 있는 것 같다.
- 일관성은 매우 중요하지만 융통성도 가져야한다는 점이 중요한 생각이다. 0과 1로 떨어지는 규칙을 만들고 싶지만, 실제로는 그렇게 되지 않는다. 그래서 어려움이 생기는 것 같고 이런 지점들에서 고민과 토론을 많이 해야할 것 같다.
- 감정적인 결론을 내리기보다 트레이드오프의 관점을 유지하는 것이 옳게 느껴졌다. 약간 기계같기도 하고. 이런 관점에서 판단을 내리는 것 자체도 훈련이 되어야할 것 같다.
스터디 코멘트
일관성이 왜 중요할까?
- 사람은 결국 맥락을 최대 7개정도 이해한다고 한다. 이런 맥락을 style때문에 잃고싶지 않은 것이다. 일관되게 되면 패턴은 넘어갈 수 있다. 그래서 일관성이 중요하다고 생각한다. 어떤 식으로 맞추느냐는 의미없다. 맞추는 것 자체가 의미있다.
- 에러처리. 200, 400. 하지만 일관성을 지키면 된다.