한 줄 정의

코드 리뷰는 버그를 잡아내는 검사 장치가 아니라 정확성·이해 용이성·일관성·주인의식·지식 공유·변경 이력을 한 번에 확보하는 구글의 최우선 개발 워크플로 이며, 작은 변경·빠른 피드백·최소한의 리뷰어·자동화라는 모범 사례를 엄격히 지켜야만 조직 규모에 맞게 확장됩니다.

쉽게 말하면

코드 리뷰는 출판 전에 원고를 편집자에게 보여주는 일 과 같습니다. 편집자의 역할은 오탈자(버그)를 잡는 데서 끝나지 않습니다. 편집자는 ‘첫 번째 독자’로서 작가가 스스로는 절대 할 수 없는 질문 — “이 글이 다른 사람에게도 읽히는가?” — 을 대신 던져줍니다. 코드도 마찬가지입니다. 코드는 작성되는 횟수보다 읽히는 횟수가 몇 배는 많으므로, 작성자의 머릿속에서만 말이 되는 코드는 커밋되기 전에 걸러져야 합니다.

그래서 이 장의 핵심은 ‘버그 잡는 법’이 아닙니다. 구글이 코드 리뷰에서 가장 기대하는 것은 정확성이 아니라 코드가 ‘내 것’에서 ‘조직의 것’이 되는 것 입니다. 다른 사람의 눈을 한 번 거치는 순간 코드는 읽히는 글이 되고, 리뷰어의 지식이 작성자에게 흘러가고, 작성자는 비판을 받아들이는 법을 배웁니다. 버그를 잡는 건 그 과정에서 따라오는 가장 눈에 띄는 부산물일 뿐입니다.

다만 모든 변경에 편집자를 거치게 하면 출판이 멈춰버릴 수 있습니다. 그래서 구글은 강제하되 철저히 가볍게 유지합니다. 완벽한 원고가 아니라 더 나아진 원고면 통과시키고, 편집자는 한 명이면 충분하고, 원고(변경)는 짧게 쪼개고, 맞춤법 검사(포맷팅·린트)는 기계에 맡깁니다. 무겁게 만들면 지속할 수 없다 는 것이 이 장 전체를 관통하는 운영 원칙입니다.

왜 중요한가?

구글의 문화는 다른 소프트웨어 회사들처럼 엔지니어에게 폭넓은 자유를 허락합니다. 신기술에 신속하게 대응해야 하는 역동적인 회사에 엄격한 프로세스는 적합하지 않고, 창의적인 직업에 관료적인 규칙은 해가 된다고 보기 때문입니다. 그런데 코드 리뷰는 이 문화 속에서도 모든 엔지니어가 반드시 따라야 하는 몇 안 되는 전사적 프로세스 입니다. 아무리 작은 변경이라도 거의 모든 변경에 코드 리뷰를 요구합니다.

이 강제는 공짜가 아닙니다. 비용을 유발하고 엔지니어링 속도에 영향을 주며, 필요한 변경을 제때 반영하기 어렵게 할 수도 있습니다. 그럼에도 강제하는 이유는 정확성 확인, 이해 용이성, 일관성, 주인의식, 지식 공유, 변경 기록이라는 이점들이 소프트웨어 조직이 오래 존속하는 데 꼭 필요 하기 때문입니다.

흥미로운 점은, ‘버그가 코드베이스로 침투하기 전에 잡아낸다’는 가장 확실하고 납득하기 쉬운 이점이 구글이 코드 리뷰에 기대하는 최우선 이점이 아니라는 것 입니다. 정확성 검사는 변경이 의도대로 작동함을 보장해줄 뿐입니다. 더 중요한 이점은 새로운 코드가 이해하기 쉽고, 세월이 흘러 코드베이스가 커져도 여전히 의미가 통할 것이라는 점입니다. 이 측면은 코드가 논리적으로 ‘정확하다’거나 당장 이해된다는 것만 살피는 것으로는 평가할 수 없습니다.

핵심 내용

코드 리뷰 흐름

구글에서는 변경을 코드베이스에 커밋하기 전에 리뷰를 수행하며, 이 단계를 프리커밋 리뷰(precommit review) 라고 합니다. 코드 리뷰의 최종 목표는 다른 엔지니어로부터 해당 변경을 적용해도 된다는 합의를 이끌어내는 것입니다. 합의한 엔지니어는 ‘좋아 보임(looks good to me)‘이라는 뜻의 LGTM 태그를 달아 의사를 표현하고, 이 LGTM이 (다른 ‘필수 항목’들과 함께) 변경을 커밋하는 데 요구되는 필수 항목입니다.

LGTM이라는 표현의 뉘앙스

리뷰 승인 용어로 다소 약한 뉘앙스의 ‘looks good to me’가 쓰인 것이 의아할 수 있습니다. ‘to me’는 각자가 자기가 맡은 영역에 한정해서 리뷰한다는 의미이고, ‘looks good’은 코드의 완벽한 품질보다 개선을 중시하는 문화에서 비롯되었습니다. 구글 코드 리뷰의 중요한 대원칙은 ‘코드가 완벽하지 않더라도 전체적인 품질을 높이는 상태에 도달했다면 리뷰어는 그 코드를 승인하는 방향으로 검토한다’ 입니다.

구글은 코드 리뷰를 보통 다음 절차대로 진행합니다.

  1. 작성자가 자신의 작업 공간에서 변경사항을 작성하고 스냅샷 을 만듭니다. 스냅샷은 리뷰 도구에 업로드할 코드 패치와 설명으로, 현재 코드베이스와의 diff를 떠서 코드가 어떻게 달라졌는지 평가합니다.
  2. 작성자는 초기 패치로 자동 리뷰 의견을 받거나 스스로 리뷰를 해본 뒤, 만족스럽다면 한 명 이상의 리뷰어에게 리뷰를 요청합니다.
  3. 리뷰어들은 리뷰 도구에서 변경을 열고 diff에 댓글로 의견을 남깁니다. 해결해야 할 문제를 던져주거나 단순히 유용한 정보를 제공합니다.
  4. 작성자는 피드백을 기초로 변경을 수정하고 새 스냅샷을 업로드한 후 회신합니다. 3~4단계는 여러 차례 반복될 수 있습니다.
  5. 리뷰어들이 모두 만족하면 LGTM 태그를 달아줍니다. 모든 리뷰어가 달아주는 게 관례지만 원칙적으로는 한 개만 얻어도 됩니다.
  6. LGTM이 달리고 모든 댓글을 해결하고 변경이 승인되면 코드베이스에 커밋할 수 있습니다.

코드는 부채다

코드는 그 자체로 부채입니다. 없어서는 안 될 부채이긴 하지만, 존재만으로 어느 순간 누군가가 유지보수해야 할 대상이 되어버립니다. 비행기 연료처럼 비행기를 띄우려면 반드시 필요하지만 그 자체가 하중을 늘려 부담으로 작용하는 것입니다.

그래서 개발을 시작하기 앞서 정말 새로운 기능이 맞는지를 주의 깊게 살펴야 합니다. 중복 코드는 작성하는 시간 자체도 낭비지만, 사실 아예 존재하지 않을 때보다 관리 비용이 날이 갈수록 더 늘어납니다. ‘밑바닥부터 만들고 있다면 분명 잘못하고 있는 거야’라는 말이 있을 정도입니다. 특히 라이브러리나 유틸리티 코드는 구글 정도의 규모라면 누군가 이미 비슷한 코드를 작성해놓았을 가능성이 크므로, 중복 확인이 먼저 이루어져야 하고 새로운 걸 설계할 때는 코드 작성 전에 관련 그룹과 대화를 나눠야 합니다.

그리고 코드 리뷰는 전에 내린 설계를 번복하거나 재논의하는 자리여서는 안 됩니다. 설계 결정에는 보통 시간이 걸립니다 — 설계 후보를 수차례 제안하고 API 리뷰 회의에서 토론하고 프로토타입을 만들어보기도 합니다. 전혀 새로운 코드를 리뷰하는 일이 뜬금없이 생겨서는 안 되듯이, 코드 리뷰 과정 자체를 기존 결정을 다시 논의할 기회로 보아서는 안 됩니다.

구글의 세 가지 승인

구글에서는 어떤 변경이든 ‘승인’을 얻으려면 세 가지 측면에서의 리뷰를 통과해야 합니다.

승인누가무엇을 검토하는가
정확성·이해 용이성 (LGTM)다른 엔지니어 (주로 팀원)작성자가 의도한 작업을 코드가 적절하게 수행하는지
코드 소유자 승인변경되는 디렉터리의 소유자변경 코드가 자신이 맡은 코드베이스에 적합한지, 체크인해도 좋은지
가독성 승인해당 언어의 가독성 인증자언어의 스타일과 모범 사례를 따르고 조직이 기대하는 방식으로 작성되었는지

변경 작성자가 코드 소유자라면 소유자 승인은 묵시적으로 받은 게 되고, 가독성 인증자라면 가독성 승인 역시 묵시적으로 이루어집니다. 그래서 변경 하나에 이렇게까지 통제하는 게 과해 보일 수 있지만, 대부분의 리뷰는 세 역할을 모두 수행할 수 있는 사람 한 명이 처리하기 때문에 빠르게 진행됩니다. 작성자가 소유자이자 가독성 인증자라면 LGTM을 달아줄 엔지니어 한 명만 추가로 찾으면 됩니다. 반대로 이런 권한이 없는 인턴사원도 가독성 인증을 받은 코드 소유자의 허가만 얻으면 서브밋할 수 있습니다. 세 가지 승인 조건은 어떤 형태로 조합되든 상관없습니다.

승인을 두 개 이상 얻어야 하는 리뷰 대부분은 두 단계로 진행됩니다. 먼저 동료 엔지니어로부터 LGTM을 받고, 그다음 코드 소유자와 가독성 인증자에게 승인을 요청합니다. 이 방식은 두 역할의 사람들이 각기 다른 측면에 집중해 리뷰하도록 해주어 시간을 절약해줍니다. 동료 엔지니어는 코드가 정확한지와 변경이 유효한지에 집중합니다. 이어서 코드 소유자는 한 줄 한 줄 살펴보는 대신(이 일은 앞 단계에서 끝났으므로) 자신이 맡은 코드베이스에 적합한 변경인지에 집중합니다 — ‘이 코드는 유지보수하기 쉬운가?’, ‘내게 기술 부채를 안겨주나?’, ‘우리 팀원 중에 이 코드를 유지보수해줄 전문가가 있나?’

한 사람이 모두 처리할 수 있는데도 역할을 셋으로 나눈 이유는 확장성 입니다. 역할을 나눔으로써 리뷰 프로세스가 더 유연해집니다. 유틸리티 라이브러리에 새 함수를 추가한다면, 팀원에게 정확성·이해 용이성 검토를 받아 LGTM을 얻은 뒤 라이브러리 소유자의 승인만 얻어내면 됩니다. 역할이 구분되어 있지 않다면 LGTM을 받기까지의 긴 과정에도 소유자가 참여해야 해서 소유자의 시간을 잡아먹고 소유자가 병목이 됩니다.

소유권과 OWNERS 파일

전용 리포지터리를 쓰는 작은 팀은 팀원 누구나 어디든 접근하게 하는 게 일반적이지만, 팀이 커지면 이 방식으로는 지탱하기 어려워집니다. 그래서 리포지터리의 각 부분을 책임져줄 사람을 명시하는 방식으로 전환하는데, 구글은 이 지식과 책임을 소유권(ownership), 행사하는 사람을 소유자(owner) 라고 부릅니다. 해당 영역의 소스 코드가 소유자의 것이라는 뜻이 아니라, 회사가 추구하는 가치가 지켜지도록 관리한다는 의미 의 소유입니다 (용어를 다시 정한다면 담당자(steward)가 더 적절할 것입니다).

  • 디렉터리의 OWNERS 파일 에 해당 디렉터리의 소유자 이름이 나열됩니다. 한 디렉터리의 소유자는 자동으로 모든 하위 디렉터리도 소유합니다.
  • 하위 디렉터리에 별도의 OWNERS 파일이 있으면 계층적으로 추가됩니다. 특정 디렉터리의 소유자는 그 디렉터리 자체와 모든 상위 디렉터리의 OWNERS 파일에 기록된 모두입니다.
  • 몇 명을 추가하느냐는 팀의 자유지만, 되도록 적은 인원으로 제한하여 책임이 명확히 드러나도록 권합니다.

소유자에게는 권한뿐 아니라 책임이 부과됩니다. 담당 코드를 본인이 잘 이해하고 있거나, 잘 이해하고 있는 사람을 찾아낼 줄 알아야 합니다. 소유권을 통과 의례처럼 사용하는 것은 바람직하지 않고, 팀을 떠난 사람은 가능한 한 빨리 소유권을 넘겨주는 게 좋습니다.

이 분산된 소유권 구조는 OWNERS 파일이 일종의 안내장처럼 작용하여 사람이나 도구가 특정 코드의 책임자를 찾기 쉽게 해줍니다. 새 프로젝트를 만들 때도 소유권 정보를 다른 곳에 등록할 필요 없이 OWNERS 파일만 만들면 충분합니다. 단순하지만 강력한 이 메커니즘은 20년 넘게 잘 확장되어, 수만 명의 구글 엔지니어가 수십억 라인의 코드를 단일 리포지터리에서 효율적으로 관리할 수 있는 비결 중 하나입니다.

코드 리뷰의 이점

잘 설계된 코드 리뷰 프로세스와 코드 리뷰를 중요하게 다루는 문화가 주는 대표적인 이점은 다음과 같습니다.

  • 코드가 정확한지 확인해줍니다.
  • 변경된 코드를 다른 엔지니어도 잘 이해합니다.
  • 코드베이스가 일관되게 관리됩니다.
  • 팀이 소유권(주인의식)을 더 강하게 느낍니다.
  • 지식이 공유됩니다.
  • 코드 리뷰 자체의 기록이 남습니다.
코드 정확성

리뷰어는 일차적으로 변경된 코드의 ‘정확성’을 확인해줍니다. 변화를 주시하는 시선이 하나 더 있다는 사실은 변경이 의도대로 이루어지도록 돕습니다. 리뷰어는 해당 변경에 적합한 테스트가 갖춰졌는지, 설계는 적절한지, 정확하게 동작하고 효율적인지를 살핍니다. IBM의 한 연구에 따르면 결함을 프로세스 초반에 잡아낼수록 나중에 발견해 고칠 때보다 시간이 덜 듭니다. 코드 리뷰에 들이는 시간은 테스트, 디버그, 회귀 테스트에 투입되는 시간을 줄여줍니다 — 단, 코드 리뷰 프로세스 자체를 단순화하여 가볍게 유지해야만 합니다. 프로세스가 무겁거나 확장하기 어렵다면 코드 리뷰를 지속할 수 없습니다.

정확성 평가가 주관적으로 흘러가지 않도록, 일반적으로 변경 작성자가 선택한 방식을 존중해줍니다. 리뷰어는 자신이 선호한다는 이유로 다른 안을 주장해서는 안 되며, 이해하기 더 쉽거나(덜 복잡하거나) 기능을 개선하는(더 효율적인) 대안일 경우에만 제시해야 합니다. 구글은 새로운 코드가 ‘완벽하다’고 합의될 때까지 기다리지 않고 코드베이스를 개선한다고 인정되면 변경을 승인하도록 안내합니다. 코드 리뷰의 속도를 높이는 수단 중 하나입니다.

도구가 발달하면서 정적 분석이나 자동화 테스트가 많은 정확성 검사를 자동으로 수행하지만, 초기 코드 리뷰 프로세스에서 결함을 찾는 일은 여전히 원점 회귀(shift left) — 문제를 가능한 한 일찍 찾아 해결함으로써 결함으로 인한 비용을 최소로 낮추는 전략 — 에 필수입니다. 다만 코드 리뷰는 결함에 대처하는 여러 방어 수단 중 한 축일 뿐 만능 치트키도, 정확성을 검사하는 유일한 수단도 아닙니다. 따라서 코드 리뷰가 ‘완벽’할 필요까지는 없습니다.

코드 이해 용이성

코드 리뷰는 작성자 외의 누군가가 변경을 살펴볼 수 있는 첫 번째 기회입니다. 리뷰어는 최고의 엔지니어라도 혼자서는 할 수 없는 일 — 작성자의 관점에 치우치지 않은 피드백 — 을 해줄 수 있습니다. 코드 리뷰는 주어진 변경이 수많은 다른 사람에게도 쉽게 이해되는지를 평가하는 첫 번째 시험대입니다. 코드는 작성되는 횟수보다 읽히는 횟수가 몇 배는 많으므로 이해하기 쉽게 작성하는 게 매우 중요합니다.

그래서 작성자와 관점이 다른 리뷰어, 특히 제안된 변경을 사용하거나 유지보수해야 할 리뷰어를 물색해보는 것도 좋습니다. 리뷰어는 작성자가 선택한 설계를 존중해야 하지만, 코드가 잘 이해되지 않으면 ‘고객은 항상 옳다’는 관점에서 질문을 던지는 게 맞습니다. 리뷰어의 의문 하나하나는 시간이 지날수록 가치가 몇 배로 커지기 때문입니다. 비판이 있다고 해서 작성자가 기존 접근법이나 로직을 꼭 바꿔야 하는 건 아닙니다. 다만 설명을 더 명확하게 해야 할 필요는 있을 것입니다.

한 리뷰어가 LGTM을 주었다는 것은 해당 코드가 의도한 일을 정확히 수행하면서 이해하기도 쉽다는 뜻입니다. 하지만 구글은 여기에 그치지 않고 지속해서 유지 가능한 코드인지까지 검토해달라고 요구하며, 필요하면 또 다른 승인을 거치도록 합니다.

코드 일관성

규모가 커지면 여러분이 작성한 코드를 결국 다른 사람이 이용하고 유지보수하게 됩니다. 여러분이 다른 프로젝트로 이동하고 나서는 (자동화 도구도 포함해) 다른 이들이 코드를 리팩터링해야 할 수도 있습니다. 따라서 코드는 일정한 표준을 따라야 하며, 너무 복잡해져서도 안 됩니다. 리뷰어는 리뷰 과정에서 코드가 코드베이스의 표준을 얼마나 잘 따르는가를 평가하여 코드의 건실함(health)을 보장합니다.

LGTM(코드가 정확하고 이해하기 쉬움)과 가독성 승인을 분리한 이유는 유지보수성 때문입니다. 가독성 승인은 오직 해당 언어의 코드 가독성 훈련 프로세스를 이수한 사람만 할 수 있습니다. 예를 들어 자바 코드는 ‘자바 가독성 인증’을 받은 엔지니어만 승인할 수 있습니다. 가독성 승인자는 주어진 코드가 다음을 만족하는지 검토합니다.

  • 해당 프로그래밍 언어의 모범 사례를 잘 따라야 합니다.
  • 구글 코드 리포지터리에서 같은 언어로 작성된 다른 코드들과 일관되어야 합니다.
  • 필요 이상으로 복잡하지 않아야 합니다.

코드는 일관되고 단순해야 사람들이 쉽게 이해할 수 있고 리팩터링 도구가 더 쉽게 다룰 수 있습니다 — 더 탄력 있는 코드가 됩니다. 코드 작성은 단 한 번일지라도 앞으로 수십, 수백, 심지어 수천 번은 읽힐 것입니다. 그래서 때로는 일관성을 위해 기능성을 희생해야 할 때도 있습니다. 가독성 리뷰어가 기능적으로는 다소 부족하더라도 이해하기 쉬운 덜 복잡한 코드를 선호할 수도 있기 때문입니다.

코드베이스가 일관되면 다른 팀의 프로젝트 코드도 한결 수월하게 리뷰할 수 있습니다. 해당 분야의 전문가를 찾아 다른 팀의 리뷰에 초대받았을 때, 이미 다른 코드들과 일관되게 작성되어 있다는 걸 알면 리뷰어는 곧바로 정확성과 이해 용이성에 집중할 수 있습니다.

심리적, 문화적 이점

코드 리뷰는 소프트웨어 엔지니어에게 코드는 ‘자신의 것’이 아니라 협업을 통해 만들어지는 ‘조직의 공동 소유물’임을 인식시켜주는 효과 가 있습니다. 미묘하지만 중요한 심리적 이점입니다. 코드 리뷰를 하지 않는다면 대다수 엔지니어가 자연스럽게 각자의 취향대로 소프트웨어를 설계하게 됩니다.

자신의 솜씨를 뽐내고 싶고 비판받는 자리에 자기 코드를 내어놓고 싶어 하지 않는 것이 사람의 본성입니다. 코드 리뷰는 이럴 때 자칫 감정적으로 번질 수 있는 논쟁을 건전하게 만들어줍니다. 제대로 작동하는 코드 리뷰는 작성자가 당연하게 여기던 가정이 틀릴 수 있음을 사전에 규정된 중립적인 방식으로 지적할 수 있게 해줍니다. 코드 리뷰 프로세스는 리뷰어들에게 비판적으로 검토해달라고 만든 제도입니다 — 그래서 구글의 코드 리뷰 도구 이름이 Critique (비평, 평론)입니다. 리뷰어들이 주어진 역할대로 비판한다고 해서 비난하면 안 됩니다. 프로세스 자체가 ‘나쁜 경찰’ 역할을 맡아주는 덕분에 리뷰어는 여전히 ‘좋은 경찰’로 남을 수 있습니다.

구글에 혹은 팀에 새로 합류한 많은 엔지니어가 코드 리뷰에 겁을 냅니다. 비판적인 리뷰를 받으면 업무 평가가 나빠지지 않을까 하는 우려 때문입니다. 하지만 점차 많은 엔지니어가 리뷰 요청을 보내면서 비판을 예상하고, 오히려 값진 조언과 질문을 받을 수 있어 유용하다고 생각하게 변합니다.

코드 리뷰가 선물하는 또 다른 심리적 이점은 검증(validation) 입니다. 가장 뛰어난 엔지니어조차 가면 증후군을 겪거나 자기비판이 너무 심할 수 있습니다. 코드 리뷰는 그들의 작업 결과를 검증하고 인정해주는 효과가 있습니다. 엔지니어의 도메인 지식이 깊어질수록 한 단계 더 성장시키는 긍정적인 피드백을 주기가 어려워질 때가 많은데, 이럴 때 코드 리뷰 프로세스가 필요한 메커니즘을 제공합니다.

마지막으로, 코드 리뷰를 필수라고 못박으면 작성자들에게 자신의 코드를 한 번 더 들여다보게 하는 효과 가 생깁니다. 코드 리뷰라는 장치가 없다면 우리 중 많은 수가 자연스럽게 절차를 무시하고, 사소한 결함은 나중에 해결하겠다는 안이함으로 넘어갈 것입니다 — ‘단위 테스트가 충분하지 않은 건 인정해. 하지만 나중에 할 거야’처럼 말이죠. 코드 리뷰는 변경을 제출하기 전에 이런 문제들을 스스로 해결하게끔 해줍니다. 리뷰 요청 직전의 짧은 시간이 자신이 변경한 것을 다시 훑어보고 혹시나 빠진 건 없는지 확인하기에 가장 완벽한 시간입니다.

지식 공유

코드 리뷰의 이점 중 정말 중요하지만 제대로 인정받지 못하는 것이 지식 공유입니다. 대부분의 작성자는 분야 전문가를 리뷰어로 선정하므로, 리뷰 프로세스는 제안, 신기술 소개, 조언을 통해 리뷰어가 변경 작성자에게 도메인 지식을 전파하도록 이끌어줍니다 (리뷰어는 작성자를 도와줄 생각으로 가볍게 FYI(for your information) 주석을 남길 수 있습니다). 이렇게 성장하여 한 분야에 아주 능숙해진 작성자는 그 영역의 소유자가 되기도 하며, 향후에 다른 엔지니어들을 위한 차세대 리뷰어가 되어줍니다.

실제로 많은 코드 리뷰에서 양방향 정보 교환이 이루어집니다. 작성자는 물론 리뷰어도 새로운 기술이나 패턴을 배울 수 있습니다. 구글에서는 코드 리뷰 도구 자체에서 리뷰어가 코드를 편집해 제안할 수도 있습니다.

엔지니어가 메일을 걸러 읽는 일은 많아도 코드 리뷰 요청에 응답하지 않는 경우는 거의 없습니다. 리뷰를 통한 지식 공유는 지역, 국가, 프로젝트 경계에 구애받지 않고 코드베이스 구석구석의 모든 엔지니어에게 빠르게 전파됩니다. 코드 리뷰야말로 곧바로 적용할 수 있는 지식을 적시에 전달해주는 완벽한 지식 전파 기회입니다 (구글에서는 다른 엔지니어와의 첫 만남이 코드 리뷰를 통해서일 때가 많습니다).

더욱이 모든 변경이 결국 코드베이스에 쌓이므로 코드 리뷰는 코드베이스의 변경 이력을 기록하는 역할 도 합니다. 엔지니어라면 누구나 코드베이스를 살펴보면서 특정 패턴이 도입된 시기와 당시의 코드 리뷰에서 어떤 질문이 오고 갔는지 확인할 수 있습니다. 원작자와 당시 리뷰어들에 그치지 않고 시간을 뛰어넘어 훨씬 많은 엔지니어에게까지 지혜를 전달해줍니다.

코드 리뷰 모범 사례

코드 리뷰는 조직에 마찰과 일정 지연을 초래할 수 있는데, 이런 문제 대다수는 코드 리뷰 자체보다는 리뷰를 구체적으로 어떻게 수행하느냐 에 기인합니다. 대다수 모범 사례는 코드 리뷰를 무리 없이 확장할 수 있도록 프로세스를 날렵하게 유지하는 게 중요하다고 강조합니다.

공손하고 전문가답게

코드 이해 용이성 요건은 단 한 명의 LGTM으로 충족되지만, 리뷰어들은 해당 변경이 이번 한 번의 리뷰만 통과하면 코드베이스에 반영될 수 있음을 잘 알고 있습니다. 절대 가볍게 보고 LGTM을 남발하지 않으므로, 가장 뛰어난 엔지니어들에게도 코드 리뷰는 긴장되는 일입니다. 어떤 피드백과 비평이라도 전문가답게 건네는 일이 그래서 중요합니다.

  • 리뷰어는 작성자가 선택한 방식을 존중하고 오직 그 방식에 결함이 있을 때만 대안을 제시해야 합니다. 작성자가 다른 대안 중 특별히 더 우수한 게 없음을 설명할 수 있다면 리뷰어는 작성자의 취향을 받아들여야 합니다. 결함을 찾게 되면 (작성자와 리뷰어 모두!) 배움의 기회로 생각하면 됩니다.
  • 작성자의 방식이 잘못되었다고 가정하기 전에 그렇게 처리하게 된 이유가 무엇인지부터 물어보는 게 좋습니다.
  • 리뷰어는 신속하게 피드백해야 합니다. 구글에서는 코드 리뷰 피드백이 24시간 내에 올 거라 기대합니다. 그 안에 피드백하지 못할 것 같으면 ‘변경을 확인은 했고, 최대한 조속히 검토해보겠다’라고 응답해주는 게 좋습니다. 이것 조금 저것 조금 식의 너무 단편적인 피드백은 작성자를 짜증 나게 합니다.
  • 작성자에게도 전문가다움이 요구됩니다. ‘나’와 ‘나의 코드’를 동일시하지 말고, 내가 제안한 변경은 혼자의 것이 아닌 팀의 소유임을 잊지 마세요. 제기된 질문들에 흥분하지 말고 왜 그렇게 처리했는지 설명할 준비를 미리 해둬야 합니다.
  • 리뷰어가 단 댓글은 모두 할일(TODO item) 로 취급해야 합니다. 의문 없이 수용할 필요는 없지만 적어도 고민은 해봐야 합니다. 동의하지 않는 댓글이라면 그 사실과 이유를 알려주고, 양측 모두 대안을 제시할 기회를 준 다음에야 완료 처리해야 합니다. 의견이 다르지만 토론을 매끄럽게 이어가고 싶을 때는 대안을 제시하고 리뷰어에게 한번 더 살펴봐달라고 부탁하는 방법이 유효합니다.
  • 외부의 누군가가 여러분이 소유한 코드베이스를 변경하려는 경우에도 수용적인 자세로 응해야 합니다. 제안된 변경이 코드베이스를 개선해주는 한 ‘마땅히 개선해야 할 점을 제안해줘서 감사합니다’라며 감사를 표해줘야 합니다.
작게 변경하기

프로세스를 날렵하게 가져가기 위한 가장 중요한 모범 사례 입니다. 코드 리뷰는 단 하나의 문제만을 다루는 게 가장 이상적입니다. 구글은 거대한 변경을 지양하며, 리뷰어는 큰 변경에 대해서 거부할 권한이 있습니다. ‘작은’ 변경이라 하면 일반적으로 변경되는 코드가 약 200줄 이하 라는 뜻입니다.

작은 변경의 효과는 여러 겹입니다.

  • 변경이 크면 리뷰하는 데도 그만큼 시간이 걸리니, 작은 변경은 작성자가 기다려야 하는 시간을 줄여줍니다. 구글에서는 대부분의 변경에 대한 초기 피드백이 하루 내에 오리라 기대합니다.
  • 변경량이 적다면 버그가 생겼을 때 살펴볼 코드도 그만큼 적어집니다.
  • 초기의 작은 리뷰는 잘못된 길로 너무 멀리까지 갔다가 되돌아오는 비용도 크게 낮춰줍니다.
  • 큰 변경과 달리 리뷰받기를 기다리는 변경들이 처리되지 못하고 쌓여가는 일이 줄어듭니다.

구글에서 만들어지는 변경의 35%는 파일 하나에서 이루어집니다. 그리고 리뷰가 대부분 작게 이루어지기 때문에 거의 모든 변경이 단 한 사람으로부터만 리뷰를 받습니다. 이렇게 하지 않으면 — 공통 코드베이스를 수정하는 모든 변경에 팀 전체가 달려들면 — 프로세스 자체를 확장할 방법이 없습니다. 대부분의 리뷰 요청은 특정 팀원 한 명에게 보내고 관련된 다른 팀들에는 참조(CC)로 전달합니다. 1차 리뷰어로부터만 LGTM을 받으면 충분하고, 다른 댓글들은 중요한 댓글일지라도 선택사항입니다.

변경이 작다면 ‘승인’을 책임지는 리뷰어들도 승인 여부를 더 신속하게 판단할 수 있습니다. 앞서 리뷰한 사람이 제 역할을 착실히 했는지 빠르게 훑어본 다음, 해당 변경이 코드베이스를 건실하게 개선해주며 오래도록 유지보수하기에 문제가 없는지에만 집중해 판단하면 됩니다.

다만 커다란 기능을 새로 도입할 때는 작은 변경들로 대응하기가 어렵습니다. 작게 나뉜 변경들은 하나씩 소화하기에는 쉽지만 전체 그림 안에서 종합적으로 검토하기는 더 어렵습니다. 큰 변경을 관리하는 기법(별도 브랜치에서 개발 후 병합, HEAD 브랜치와의 diff 기반 관리)도 있지만 부담이 늘어나는 건 피할 수 없습니다. 프로세스를 작은 변경에 적합하게끔 최적화하는 동시에 가끔 일어나는 큰 변경도 수용할 수 있게 하면 좋을 것입니다.

변경 설명 잘쓰기

변경 설명의 첫 줄 은 어떤 종류의 변경인지를 잘 요약해야 합니다. 코드 리뷰 도구가 읽어가서 마치 이메일의 제목처럼 이용하고, Code Search 도구도 변경 이력을 표시할 때 각 변경의 첫 줄을 보여주기 때문입니다.

첫 줄이 전체를 요약해주지만, 구체적으로 무엇을 왜 변경하는지 알려주는 자세한 설명 역시 필요합니다. 변경 설명에 달랑 ‘버그 수정’이라고만 적어놓으면 리뷰어는 물론이고 나중에 변경 이력을 살펴보는 사람들에게도 아무런 도움이 못 됩니다. 연관된 수정 여러 개를 포함한 변경이라면 그 모두를 간략하게 열거합니다. 설명은 해당 변경의 이력이며, 디버깅할 때 변경 이력을 자세히 파헤쳐보면 원인이 튀어나올 때가 자주 있습니다.

변경 설명 외에도, 만약 리뷰어가 코드를 이해하지 못한다면 비록 올바르게 동작하는 코드일지라도 구조를 개선하거나 주석을 더 잘 달아놔야 한다는 신호일 수 있습니다. 코드 리뷰를 거치면서 처음 코드와 달라지는 점은 변경 설명과 코드 주석에도 반영해야 합니다. 코드 리뷰는 지금 당장만이 아니라 후대를 위해 현재 하고 있는 일을 기록하는 행위입니다.

리뷰어는 최소한으로

구글에서 이루어지는 대부분의 코드 리뷰는 정확히 한 명의 리뷰어만으로 진행됩니다. 변경 승인에 필요한 세 가지 역할을 단 한 사람이 모두 수행할 수 있게 허용하여 코드 리뷰 프로세스가 구글 정도의 규모에서도 잘 운영되게끔 확장할 수 있었습니다.

엔지니어 개개인은 다른 영역의 엔지니어들로부터 더 많은 피드백을 받아보려는 (그리고 그들 모두가 동의해주길 바라는) 경향이 있습니다. 하지만 리뷰어가 한 명 추가될 때마다 새로운 시각이 한 스푼씩 더해지며, 결국 수확 체감(diminishing returns) 으로 이어집니다. 첫 번째 LGTM이 가장 중요하며, 두 번째부터는 크게 신경 쓸 만큼의 가치가 없습니다. 리뷰어를 추가해서 얻는 가치보다 비용이 훨씬 빠르게 증가하여 금세 역전됩니다.

구글의 코드 리뷰 프로세스는 엔지니어들이 일을 올바르게 처리할 것이라는 신뢰 를 바탕으로 최적화되었습니다. 때에 따라서는 여럿이 검토하는 게 나은 변경도 있지만, 이 경우 리뷰어들은 서로 다른 관점에서 바라보도록 역할을 조율한 후 진행해야 합니다.

가능한 한 자동화하기

코드 리뷰는 사람이 주도하는 프로세스라서 사람이 주는 피드백이 중요하지만, 프로세스 중 자동화할 수 있는 요소가 있다면 자동화해야 합니다. 자동화 측면에서 지난 몇 년간 이루어진 가장 중요한 진보는 정적 검사 자동화 입니다. 구글에서는 테스트, 린터, 포맷터를 작성자가 직접 수행할 필요가 없습니다. 거의 모든 코드 리뷰 도구가 소위 프리서브밋(presubmit) 과정에서 자동으로 수행되기 때문입니다.

프리서브밋 프로세스는 변경이 제출되기 전에 다양한 검사를 수행하고, 문제가 검출되면 변경을 거부하고, 리뷰 요청이 리뷰어에게 전달되지 않게 차단하고, 작성자에게 코드 수정 후 다시 제출하라고 알려줍니다. 이런 자동화는 리뷰어가 포맷팅보다 더 중요한 문제에 집중하도록 도와줍니다.

코드 리뷰 유형

코드 리뷰 유형에 따라 리뷰 프로세스의 어느 측면에 더 집중해야 하는지가 달라집니다. 구글에서의 코드 변경은 보통 다음 네 가지 분류에 속합니다 (때로는 겹치기도 합니다).

유형리뷰에서 집중할 것
그린필드 리뷰와 새로운 기능 개발코드가 오래 존속할 수 있는지, 합의된 설계 부합, 테스트 완비, OWNERS 지정
동작 변경, 개선, 최적화꼭 필요한 변경인지, 테스트 동반 수정, 벤치마크
버그 수정과 롤백버그에만 집중, 재발 방지 테스트 보강
리팩터링과 대규모 변경(LSC)정확성·적용 가능성만 검토, 기반 도구·프로세스는 논외
그린필드 코드 리뷰

그린필드 리뷰(greenfield review) 는 완전히 새로운 코드를 대상으로 하는 가장 드문 유형입니다. 대상 코드가 오랜 기간 존속될 수 있는지를 평가하기에 가장 중요한 기회입니다. 코드는 부채이므로, 완전히 새로운 코드가 단순히 또 하나의 대안 제시에 머물러서는 안 되고 실제 문제를 해결해주는 코드여야 합니다.

구글에서는 보통 새로운 코드나 프로젝트에는 코드 리뷰와 별개로 설계 리뷰를 강도 높게 진행합니다. 따라서 코드 리뷰는 이미 결정된 설계에 관해서 왈가왈부하는 시간이 아닙니다 (같은 이유에서 대안 설계를 제시하는 자리도 아닙니다).

그린필드 리뷰에서 확인해야 할 것들은 다음과 같습니다.

  • API가 합의된 설계에 부합하는지 (설계 문서도 함께 검토하기도 합니다)
  • 공개된 모든 API에 단위 테스트가 존재하고, 이 테스트들은 코드의 가정이 달라지면 실패하는지
  • 코드를 책임질 알맞은 소유자가 배정되었는지 (새로운 프로젝트의 첫 번째 리뷰에서는 디렉터리에 OWNERS 파일이 제대로 존재하는지 확인합니다)
  • 주석이 충분한지, 필요하면 보충 문서를 제공했는지
  • 프로젝트를 회사의 지속적 통합 시스템에 포함시킬지
동작 변경, 개선, 최적화

구글에서 이루어지는 대부분의 변경은 코드베이스 안의 기존 코드를 수정하는 것입니다 — API 수정, 기존 구현 개선, 성능 최적화. 이 경우에도 그린필드 리뷰의 지침이 그대로 적용됩니다. 꼭 필요한 변경인지, 코드베이스를 개선하는지를 살펴야 합니다. 가장 바람직한 변경의 멋진 예로 ‘삭제’를 많이 거론합니다. 죽은 코드나 낡은 코드 제거는 코드베이스를 전반적으로 건실하게 만드는 아주 멋진 방법입니다.

  • API의 동작을 수정할 때는 변경된 동작에 맞게 관련 테스트도 함께 수정해야 합니다.
  • 동작 변경 없이 구현 방식만 개선한 경우라면 지속적 통합 시스템을 거쳐서 기존 테스트들이 모두 통과하는지 봐야 합니다.
  • 최적화 역시 기존 테스트들에 영향을 주지 말아야 하며, 추가로 성능 벤치마크 결과를 리뷰어에게 제시해야 할 것입니다. 그래서 최적화를 하려면 벤치마크 테스트를 미리 준비해두는 게 좋습니다.
버그 수정과 롤백

버그를 수정하면서 (기능 변경 등) 다른 문제까지 묶어 처리하고픈 마음을 꾹 눌러야 합니다. 여러 주제가 섞이면 리뷰할 게 많아지는 건 둘째 치고, 회귀 테스트나 롤백을 훨씬 어렵게 만듭니다. 버그 수정은 온전히 그 버그를 잡는 데만 집중하고, 관련 테스트도 함께 수정하여 버그가 재발할 시 알려주도록 해야 합니다.

버그 수정 시 테스트도 보강해야 할 가능성이 큽니다. 버그가 새로 발견된 이유는 기존 테스트들이 충분하지 못했거나 특정 가정이 어긋났기 때문입니다. 관련 단위 테스트에 업데이트가 필요한지 확인해서 업데이트를 요청하는 것 역시 버그 수정 변경의 리뷰어가 챙겨야 할 중요한 일입니다.

코드베이스가 거대하면 변경한 코드가 테스트들이 잡아내지 못했거나 테스트되지 않은 코드를 사용해서 오류가 나는 일이 종종 생깁니다. 이럴 때 구글은 영향받은 다운스트림 고객들이 해당 변경을 롤백 시킬 수 있게 합니다. 롤백은 변경이 반영되기 전 상태로 코드를 되돌리도록 구성한 또 하나의 ‘변경’이며, 롤백 변경은 보통 수 초면 만들 수 있지만 그래도 코드 리뷰는 필요합니다.

잠재적으로 롤백을 유발할 수 있는 모든 변경은 가능한 한 작고 원자적 이어야 합니다. 그래야 롤백으로 인해 해당 코드를 사용하던 다른 모듈이나 프로젝트들이 망가지는 문제를 막을 수 있습니다. 구글은 거의 모든 코드를 모노리포에서 투명하게 관리하기 때문에 코드를 새로 추가하면 얼마 지나지 않아 누군가 이용하기 시작하고, 롤백은 그 사이에 코드를 이용하기 시작한 엔지니어들의 작업에 지장을 줍니다. 작은 변경은 원자적이라서, 또 빠르게 리뷰할 수 있어서 이런 피해를 줄여줍니다.

리팩터링과 대규모 변경

구글에서는 많은 변경이 자동으로 생성됩니다 — 작성자가 사람이 아니라 기계라는 뜻입니다. 기계(도구)가 생성한 대규모 변경(large-scale change, LSC) 도 리뷰가 필요합니다. 위험성이 낮다고 생각되는 변경은 해당 코드에 관한 전권을 가진 리뷰어가 맡아서 처리하고, 위험도가 높거나 도메인 전문가가 필요한 변경들은 다른 엔지니어들도 리뷰에 참여합니다.

자동 생성된 변경도 리뷰어가 정확성과 적용 가능성을 확인한다는 점에서는 여느 코드 리뷰와 다르지 않습니다. 하지만 다음 제약이 있습니다.

  • 기반 도구나 변경들을 생성한 LSC 자체에는 신경 쓰지 말고 리뷰어 자신의 코드와 관련된 문제만 신경 쓰라고 안내합니다. 검토하는 특정 변경은 기계가 만들었지만, 그 변경을 생성한 전체 과정은 이미 검토가 끝난 후라서 개별 팀이 전체 프로세스를 거부할 수는 없기 때문입니다. 이런 방식이 아니면 조직 차원의 대규모 변경을 수행할 수 없습니다. 기반 도구나 프로세스에 제기할 문제가 있는 리뷰어는 LSC 감독 그룹에 연락하면 됩니다.
  • 자동 변경의 리뷰어들에게는 수정 범위를 확대하지 않기를 권합니다. 팀원이 작성한 변경이라면 추가 요청을 해도 이상하지 않지만, 도구를 운영하는 엔지니어는 수백 개의 변경을 동시에 처리해야 할 수 있습니다. 댓글이나 질문이 단 몇 %의 변경에만 달리더라도 도구를 지속해서 개선하려는 의욕을 위축시킬 수 있습니다.

비교 / 트레이드오프

일반 변경 리뷰 vs 자동 생성(LSC) 변경 리뷰
관점일반 변경자동 생성(LSC) 변경
작성자사람 (주로 팀원)기계 (도구)
리뷰 범위변경 전반 + 추가 요청 가능자신의 코드와 관련된 문제만
설계·프로세스 재논의설계 리뷰에서 이미 결정 — 코드 리뷰에서 재론 금지생성 프로세스는 이미 검토 완료 — 개별 거부 불가, 이의는 LSC 감독 그룹에
범위 확대변경을 작게 유지하는 선에서 허용지양 (도구 운영자의 개선 의욕 위축)
리뷰어 한 명 vs 여러 명
관점한 명 (구글 기본)여러 명
가치첫 LGTM이 가장 중요리뷰어 추가마다 새 시각이 한 스푼씩 — 수확 체감
비용최소, 프로세스 확장 가능가치보다 비용이 훨씬 빠르게 증가하여 금세 역전
적합한 경우거의 모든 변경 (35%는 단일 파일)여럿의 검토가 나은 변경 — 단, 서로 다른 관점을 보도록 역할을 조율한 후 진행
빠른 승인 vs 완벽한 코드

구글 코드 리뷰의 대원칙은 완벽이 아니라 개선입니다. 새로운 코드가 ‘완벽하다’고 합의될 때까지 기다리지 않고 코드베이스를 개선한다고 인정되면 승인합니다. 코드 리뷰는 결함에 대처하는 여러 방어 수단(정적 분석, 자동화 테스트, CI) 중 한 축일 뿐이므로 ‘완벽’할 필요까지는 없고, 그래서도 프로세스가 가볍게 유지됩니다. 프로세스가 무겁거나 확장하기 어렵다면 코드 리뷰 자체를 지속할 수 없습니다.

내 생각

  • 세 역할 분리는 GitHub 플로우의 CODEOWNERS + 필수 리뷰어 조합의 원형입니다. 다만 구글의 핵심은 분리 자체가 아니라 “한 사람이 세 역할을 겸할 수 있어 대부분 리뷰어 한 명으로 끝난다”는 축약 경로입니다. 사내에서 리뷰어 2명 필수 같은 규칙을 도입할 때, 수확 체감 관점에서 정말 두 번째 리뷰가 가치를 내는지 따져볼 근거가 됩니다.

  • “리뷰 댓글은 모두 TODO item”은 PR 코멘트 처리의 실질적 프로토콜입니다. resolve 버튼을 침묵으로 누르는 게 아니라, 동의하지 않으면 이유를 남기고 양측이 대안을 낼 기회를 준 뒤 닫는다 — 이것만 팀 규칙으로 합의해도 리뷰 갈등의 상당수가 사라집니다.

  • ‘삭제가 가장 바람직한 변경’은 코드는 부채다와 한 쌍입니다. 죽은 코드 제거 PR이 기능 PR보다 낮게 평가되는 조직 분위기가 있다면, 이 장이 그 인식을 뒤집는 근거가 됩니다.

  • 버그 수정에 리팩터링을 끼워 넣지 말라는 조언의 진짜 근거는 롤백 가능성입니다. 리뷰 부담보다도, 섞인 변경은 롤백 시 무관한 개선까지 함께 되돌려야 한다는 점이 치명적입니다. hotfix 브랜치에서 “온 김에 정리”하고 싶은 유혹을 누를 때 떠올릴 기준입니다.

  • 프리서브밋 자동화는 리뷰어의 주의력을 보호하는 장치입니다. 포맷·린트·테스트가 CI에서 PR 생성 시점에 강제되면 사람 리뷰는 설계·이해 용이성·유지보수성에만 쓰입니다. 리뷰어가 포맷 지적을 하고 있다면 자동화가 덜 된 것입니다.

관련 개념