리뷰 자동화 이야기를 하면 사람들은 보통 속도를 떠올린다. 리뷰가 밀려서, 며칠씩 기다려야 해서.

우리는 그 앞 단계에 있었다. 리뷰가 없었다.

각자 프로젝트를 하나씩 끌고 가는 구조에서 남의 레포를 열어 diff를 읽는 일은 언제나 내 일보다 뒤에 놓인다. 악의가 있어서가 아니다. 그저 우선순위에서 매번 진다.

혼자 담당하는 레포는 애초에 리뷰어 후보가 없었고, 사내 GitLab에는 Runner가 없어 CI가 대신 잡아주는 것도 없었다. 결과는 둘 중 하나였다. 리뷰 없이 머지되거나, 리뷰를 기다리다 잊히거나.

없는 것을 만든다

사람을 더 뽑을 수 없다면 선택지는 좁아진다. 그래서 봇을 만들었다.

사내 GitLab에 계정을 하나 만들고, 열린 MR을 찾아 로컬에서 diff를 읽고 코멘트를 다는 파이프라인을 붙였다. 여기까지는 누구나 그릴 수 있는 그림이다.

실제로 굴려보니 어려운 지점은 다른 데 있었다. 무엇을 리뷰할 것인가보다, 이 봇을 어디까지 믿을 것인가가 문제였다.

상태를 어디에 둘 것인가

봇을 회사 맥과 집 맥에서 각각 돌리면 같은 MR을 두 번 리뷰하는 사고가 난다.

처음엔 로컬 파일에 리뷰 기록을 남겨 막으려 했다. 곧 접었다. 머신이 둘이 되는 순간 로컬 기록은 자기 세계의 진실만 알고 있다.

그래서 코멘트 안에 보이지 않는 표식을 심었다. HTML 주석이라 화면에는 드러나지 않지만 API로는 조회된다. 리뷰하기 전에 그 표식을 찾아보면 된다.

기록의 정본이 GitLab 자체가 되니 어느 머신에서 돌려도 안전해졌다. 로컬 파일은 캐시로 내려갔다.

분산된 곳에서 같은 일을 하는 여럿이 있을 때, 진실을 어디에 두느냐가 대부분의 문제를 결정한다는 걸 이때 다시 배웠다.

침묵할 줄 아는 것

리뷰가 끝난 뒤 커밋이 더 쌓인 MR을 처음부터 다시 리뷰하면, 이미 한 말을 또 한다. 받는 사람에게 그건 리뷰가 아니라 소음이다.

그래서 표식에 담긴 이전 시점부터의 변경만 본다. 그리고 이전에 남긴 지적이 반영됐는지도 그때 확인한다.

한 가지 더 정한 게 있다. 확신이 서지 않는 지적은 게시하지 않는다. 나에게만 보고하고 끝낸다.

자동화된 코멘트는 사람의 코멘트보다 무게가 가볍다. 틀린 지적이 몇 번 쌓이면 사람들은 봇의 말을 읽지 않게 되고, 그다음부터는 맞는 지적도 지나친다. 말할 수 있는 것과 말해야 하는 것은 다르다.

봇이 할 수 없는 일

봇의 토큰은 코멘트만 달 수 있다. 머지도 승인도 라벨도 못 하고, git에는 아무것도 쓰지 못한다. 정해진 그룹 밖은 아예 보지 않는다.

자동화를 설계할 때 기능 목록만큼 오래 붙들고 있었던 게 금지 목록이었다. 자동화가 한 번 사고를 내면 그 도구는 다시 쓰이지 않는다. 편의는 조금씩 쌓이지만 신뢰는 한 번에 무너진다.

리뷰 기준도 봇 안에 넣지 않고 문서로 뺐다. 기준을 바꾸고 싶을 때 코드를 고치는 대신 문서를 고친다. 그러면 그건 나만 할 수 있는 일이 아니게 된다.

무엇이 남았나

이제 모든 MR이 최소한 한 번은 읽힌다. 혼자 담당하는 레포도 예외가 아니다.

기계적인 지적을 봇이 먼저 깔아주니, 사람이 하는 리뷰는 자연스럽게 설계와 맥락 쪽으로 올라갔다. 왜 이 화면이 이렇게 생겨야 하는지, 이 결정이 다음 분기에 어떤 빚이 되는지 같은 것들.

가장 크게 달라진 건 사실 기술이 아니라 마음이었다. 부탁할 사람이 없어서 그냥 머지하던 시절의 찝찝함이 사라졌다.

그리고 남은 질문이 하나 있다. 우리가 없앤 것은 리뷰의 부재였을까, 아니면 리뷰를 부탁하기 어려웠던 관계의 문제였을까.

기계가 할 수 있는 리뷰를 기계에 넘겼더니, 사람의 리뷰가 필요한 자리가 오히려 또렷해졌다.