💡 Key Takeaways
- The Psychology of Code Review: Why Most Reviews Fail Before They Start
- The Reviewer's Checklist: What to Actually Look For
- The Art of the Constructive Comment
- Being Reviewed: How to Make Your PRs Reviewable
나는 소프트웨어 공학을 그만두게 만들 뻔한 코드 리뷰를 아직도 기억한다. 2012년, 나는 핀테크 스타트업에서 첫 직장에 들어온 지 6개월이 되었고, 우리의 결제 처리 시스템을 훌륭하게 리팩토링했다고 생각하며 코드를 제출했다. 선임 엔지니어의 리뷰는 47개의 코멘트로 돌아왔는데, 그 대부분이 "이건 잘못됐다"는 설명도 없는 변형 버전이었다. 나는 모든 것을 다시 쓰는 데 3일을 소비했지만, 같은 리뷰어가 단 한 마디로 승인했다: "좋다." 이 경험은 더 나은 코드를 작성하는 것에 대해 아무 것도 가르쳐 주지 않았지만, 코드 리뷰를 어떻게 하면 안 되는지를 교훈 주었다.
💡 주요 내용
- 코드 리뷰의 심리학: 대부분의 리뷰가 시작되기 전에 실패하는 이유
- 리뷰어의 체크리스트: 실제로 무엇을 찾아야 하는가
- 건설적인 댓글의 기술
- 리뷰를 받는 법: PR을 리뷰할 수 있게 만드는 방법
12년이 흐르고, 지금 나는 Series C SaaS 회사의 수석 엔지니어로 재직 중이며, 8000개 이상의 풀 리퀘스트를 리뷰하고 40명 이상의 개발자에게 코드 리뷰 프로세스를 멘토링 해왔다. 나는 코드 리뷰가 회사들을 치명적인 버그로부터 구한 것을 보았고, 팀 사기를 완전히 망가뜨리는 것을 보기도 했다. 팀이 6개월 이내에 완전히 교체된 경우도 있었다. 그 차이는 무엇일까? 리뷰되고 있는 코드의 품질이 아니라, 리뷰 프로세스 자체의 품질이다.
코드 리뷰는 소프트웨어 개발에서 가장 강력한 도구이자 가장 자주 오용되는 도구이기도 하다. SmartBear의 2023년 코드 리뷰 상태 보고서에 따르면, 구조화된 코드 리뷰 프로세스를 구현한 팀은 프로덕션 이전에 60-90%의 결함을 찾아내지만, 같은 연구에서는 73%의 개발자가 자신감을 잃거나 동료와의 관계에 피해를 준 부정적인 경험을 보고한다고 한다. 이와 같은 역설은 대부분의 팀이 WHAT을 리뷰하는 데 초점을 맞추는 반면, HOW을 리뷰하는 데는 신경 쓰지 않기 때문에 발생한다.
코드 리뷰의 심리학: 대부분의 리뷰가 시작되기 전에 실패하는 이유
코드 리뷰에 대해 아무도 말해주지 않는 것이 있다: 이는 주로 코드에 관한 것이 아니다. 이는 사람에 관한 것이다. 각 풀 리퀘스트는 누군가의 지적 노력, 창의적인 문제 해결, 그리고 전문적인 정체성을 나타낸다. 코드를 리뷰할 때, 단순히 논리와 구문을 평가하는 것이 아니라, 다른 사람의 작업 산출물과 대화하게 된다. 이는 상대방을 고양시키거나 무너뜨릴 수 있다.
나는 이 사실을 재직 중인 팀에서 떠난 유능한 주니어 개발자와의 퇴사 인터뷰를 통해 어렵게 배웠다. 그녀는 단 한 번의 무시하는 코드 리뷰 댓글—"왜 이렇게 하려고 하셨나요?"—이 그녀가 공학에 적합한 사람인지 의문을 갖게 했다고 말했다. 리뷰어는 그녀의 reasoning에 대해 진지한 질문을 던진 것이었지만, 그녀는 그것을 판단으로 해석했다. 그때 나는 비동기식 텍스트 매체가 어조, 표정, 바디랭귀지를 제거하고, 최악의 해석으로 이해될 수 있는 단어만 남긴다는 것을 깨달았다.
심리학 연구도 이를 뒷받침한다. 2021년 IEEE Transactions on Software Engineering에 게재된 연구에 따르면, 혹독하거나 무시하는 것으로 인식되는 코드 리뷰 코멘트는 병합 시간을 평균 3.2일 증가시켰고, 저자가 향후 개선에 기여할 가능성을 34% 감소시켰다. 반면, 구체적인 칭찬과 건설적인 피드백을 포함한 리뷰는 28% 더 빠른 반복 사이클과 41% 높은 품질을 낳았다.
이것은 우리가 모든 것을 설탕으로 포장해야 한다는 것이나 문제를 지적하는 것을 피해야 한다는 것이 아니다. 이는 우리가 코드 리뷰를 할 때 상대방 인간에 대해 의도적으로 접근해야 한다는 것을 의미한다. 리뷰 코멘트를 작성하기 전에, 나는 세 가지 질문을 던진다: 이것이 사실인가? 이것이 필요한가? 이것이 친절한가? 세 가지 질문에 모두 '예'라고 답하지 못한다면, 코멘트를 다시 작성한다. 이 간단한 필터는 우리 팀의 의사소통 방식을 변화시켰고, 지난 2년간 평균 PR 사이클 시간을 4.3일에서 1.8일로 줄였다.
리뷰어의 체크리스트: 실제로 무엇을 찾아야 하는가
새로운 리뷰어를 교육할 때 그들은 항상 같은 질문을 한다: "무엇을 찾아야 하나요?" 대답은 단순한 구문 규칙이나 스타일 가이드의 목록이 아니다—이들은 자동화되어야 한다. 인간 리뷰어의 역할은 기계가 할 수 없는 것들: 설계 결정, 유지보수성, 비즈니스 로직의 정확성을 평가하는 것이다.
나는 내 리뷰 에너지를 가장 중요한 곳에 집중할 수 있도록 돕는 4단계 우선순위 시스템을 사용한다. Tier 1 문제는 차단 요소로, 보안 취약점, 데이터 손실 위험, 또는 프로덕션 사고를 일으킬 변경사항이다. 이러한 문제는 위험에 대한 명확한 설명과 함께 즉시 표시된다. 내 경험에 따르면, 진정한 Tier 1 문제는 풀 리퀘스트의 5% 미만에서 발견되지만, 이러한 문제가 발생할 때 그것을 잡는 것이 우리가 코드 리뷰를 하는 이유다.
Tier 2 문제는 아키텍처 문제로, 작동하지만 기술 부채를 발생시키거나, 기존 패턴을 위반하거나, 향후 변경을 어렵게 만드는 코드다. 이는 현재 코드베이스와 팀의 미래 방향을 모두 이해해야 하므로 리뷰하기 가장 까다롭다. 나는 이러한 문제를 지시사항보다는 질문으로 접근하는 것이 더 효과적이라는 것을 발견했다: "나는 이 접근 방식이 다음 분기에 기능 X를 구현하기 어렵게 만들 수 있다는 점에 우려하고 있어—패턴 Y를 사용하는 것을 고려해본 적이 있나요?" 이렇게 하면 방어적이지 않고 대화를 유도할 수 있다.
Tier 3 문제는 가독성 및 유지보수성 향상에 관한 것으로, 불분명한 변수 이름, 복잡한 로직에 대한 주석 부족, 혹은 명확성을 위해 분해할 수 있는 함수 등을 포함한다. 이러한 문제도 중요하지만 긴급하지는 않다. 나는 일반적으로 한 리뷰당 세 가지 Tier 3 코멘트로 제한하고, 향후 유지 보수성에 가장 큰 영향을 미칠 영역에 집중한다. 이것을 초과하면 저자는 압도감을 느끼고 피드백과의 상호작용이 중단된다.
Tier 4는 기타 모든 사항으로, 스타일 선호, 반드시 더 나은 것은 아닌 대안적 접근, 또는 형식에 대한 세부적인 지적이 포함된다. 여기에 대한 나의 의견은 논란의 여지가 있다: 거의 항상 Tier 4 코멘트를 남기지 말아야 한다는 것이다. 표준화할 만큼 중요하다면, 이를 린터 또는 스타일 가이드에 추가하라. 자동화할 만큼 중요하지 않다면, 풀 리퀘스트의 속도를 늦출 만큼 중요하지 않다. 나는 팀들이 실제 비즈니스 로직 오류가 있는 코드를 배포하면서 단일 또는 이중 인용부호 사용 여부를 두고 몇 시간 동안 토론하는 것을 보았다. 그런 팀이 되지 말라.
건설적인 댓글의 기술
유용한 코드 리뷰 댓글과 사기를 꺾는 댓글의 차이는 종종 몇 마디에 달려 있다. 같은 코드 조각에 대한 두 가지 코멘트를 비교해 보자:
| 리뷰 접근 방식 | 코드 품질에 미치는 영향 | 팀 사기에 미치는 영향 |
|---|---|---|
| 맥락 없는 트집 ("이건 잘못됐다") | 최소한의 개선; 저자는 기본 원칙을 배우지 않는다 | 매우 부정적; 두려움과 원한을 생성함 |
| 타당성을 읽지 않고 승인 ("LGTM") | 개선 없음; 버그가 프로덕션으로 흘러들어간다 | 단기 중립, 품질 저하로 장기적으로 부정적 |
| 이유를 설명하는 피드백 | 높은 개선; 저자는 패턴과 원칙을 배운다 | 긍정적; 신뢰와 심리적 안전성을 구축함 |
| 협력적인 토론 (질문 vs. 명령) | 매우 높음; 엣지 케이스 및 대안적 접근 방식을 표면화함 | 매우 긍정적; 지식 공유와 상호 존중을 촉진함 |
| 자동화된 점검 + 인간의 통찰 | 가장 높음; 기계적 문제를 자동으로 잡고, 인간은 아키텍처에 집중함 | 매우 긍정적; 마찰을 줄이고 의미 있는 논의에 중점을 둔 리뷰를 가능하게 함 |
"이 함수는 너무 길고 너무 많은 일을 한다." "이 함수는 검증과 데이터 변환을 모두 처리하여 각 문제를 독립적으로 테스트하기 어렵게 만든다. validateUserInput()와 transformToApiFormat()으로 나누는 것을 고려해 보라—그렇게 하면 변환을 모의하지 않고 검증 로직을 테스트 할 수 있다."
첫 번째 댓글은 기술적으로 맞지만 쓸모가 없다. 그것은 저자에게 무엇이 잘못되었는지를 말해주지만, 그 중요성이나 수정 방법에 대해서는 설명하지 않는다. 두 번째 댓글은 문제를 설명하고 그 영향을 설명하며 구체적인 해결책을 제시한다. 그것은 나에게...