이유를 모르는 코드를 지우기 전에

해커들의 용어 사전인 자곤 파일(Jargon File)에는 MIT AI 연구소에서 있었던 이야기가 하나 실려 있다. 연구소의 PDP-10 컴퓨터 캐비닛 옆면에 누가 달았는지 아무도 모르는 스위치가 하나 붙어 있었다. 스위치 양쪽에는 연필로 magic 과 more magic 이라고 적혀 있었다. 손잡이는 more magic 쪽에 가 있었다.
이야기를 남긴 사람이 스위치를 자세히 들여다보니 연결된 전선이 한 가닥뿐이었다. 전선의 다른 쪽 끝은 컴퓨터 안쪽의 접지 핀에 물려 있었다. 스위치가 무언가를 끊거나 이으려면 전선이 두 가닥은 있어야 한다. 한 가닥짜리 스위치는 아무 일도 할 수 없으니 누군가의 장난이라고 판단하고 동료와 함께 손잡이를 magic 쪽으로 넘겼다.
컴퓨터는 그 자리에서 멈췄다.
스위치는 결국 떼어냈다. 그 뒤로 컴퓨터는 별 탈 없이 돌았다. 왜 멈췄는지는 끝내 밝혀지지 않았다. 이야기는 이 스위치가 정말 magic 이었다고 말할 수밖에 없다는 문장으로 끝난다.
개발자라면 이 이야기가 낯설지 않을 것이다. 아무 일도 안 하는 것처럼 보이는 코드를 정리했다가 다음 날 아침 장애 알림을 받는 일은 생각보다 흔하다. 이번 포스팅에서는 이유를 모르는 코드를 만났을 때 어떻게 하면 좋을지 정리해보려고 한다.
체스터턴의 울타리
영국 작가 G. K. 체스터턴은 1929년 책 『The Thing』에서 길을 가로막은 울타리 하나를 예로 든다. 한 개혁가가 울타리를 보고 쓸모를 모르겠으니 치워 버리자고 한다. 체스터턴은 그 개혁가에게 이렇게 답해야 한다고 썼다.
쓸모를 모르겠다면 더더욱 치우게 둘 수 없다. 가서 생각해 보라. 그리고 그 쓸모를 알게 되어 돌아와서 내게 설명할 수 있다면, 그때는 치우게 해 줄 수도 있다.
이 비유는 지금 체스터턴의 울타리라는 이름으로 불린다. 울타리를 세우는 데는 돈과 품이 든다. 누군가 그 수고를 했다면 그때는 이유가 있었을 가능성이 높다. 그 이유가 지금도 유효한지는 이유를 알고 난 다음에야 판단할 수 있다.
코드 속의 울타리
코드에도 이런 울타리가 많다. 예를 들어 결제 화면 코드 한가운데 이런 줄이 있다고 해보자.
// 삭제 금지
await new Promise((resolve) => setTimeout(resolve, 300));300밀리초를 그냥 기다리는 코드다. 화면을 느리게 만들 뿐 하는 일이 없어 보인다. 주석에는 지우지 말라는 말만 있을 뿐 이유가 없다. 이런 줄을 보면 손이 근질거린다. 특정 사용자 아이디로 분기하는 if 문, 아무도 호출하지 않는 것 같은 함수, 똑같은 요청을 두 번 보내는 코드도 비슷한 기분을 준다.
이유가 적혀 있지 않다고 이유가 없는 건 아니다. 300밀리초 대기는 결제 대행사의 창이 늦게 뜨는 특정 브라우저를 위한 땜질일지도 모른다. 사용자 아이디 분기 뒤에는 큰 거래처와 맺은 예외 계약이 숨어 있을 수도 있다. 물론 정말 아무 이유 없이 남은 찌꺼기일 수도 있다. 어느 쪽인지는 찾아보기 전에는 모른다.
이유를 찾는 곳
다행히 코드의 울타리는 체스터턴 시대의 울타리보다 이유를 찾기가 쉽다. 대부분의 변경에는 기록이 남기 때문이다.
git log -S "setTimeout(resolve, 300)" --onelinegit log 의 -S 옵션은 주어진 문자열이 추가되거나 지워진 커밋을 찾아 준다. 그 줄이 처음 들어온 커밋을 찾으면 커밋 메시지와 함께 묶인 PR, 이슈 번호까지 따라갈 수 있다. 줄 하나만 보고 싶다면 git blame 으로 마지막으로 고친 커밋부터 거슬러 올라가도 된다.
| 찾아볼 곳 | 알 수 있는 것 |
|---|---|
| 커밋 메시지와 PR 설명 | 그 줄을 넣은 이유와 당시 논의 |
| 이슈와 장애 기록 | 그 코드가 막으려던 버그 |
| 테스트 | 그 동작에 기대는 다른 코드 |
| 그 코드를 넣은 사람 | 기록에 없는 맥락 |
마지막 줄이 가장 빠를 때가 많다. 커밋 기록을 30분 뒤지는 것보다 메신저로 한 줄 물어보는 게 낫다. 그 사람이 회사를 떠났다면 같은 팀에 오래 있었던 사람에게 물어보면 된다.
이유를 못 찾았을 때
기록을 다 뒤져도 이유가 안 나올 때가 있다. 그렇다고 영원히 남겨 둘 필요는 없다. 체스터턴도 쓸모를 알면 치워도 된다고 했지 절대 치우지 말라고 하지는 않았다. 쓸모없는 울타리도 관리 비용은 든다. 코드를 읽는 사람은 매번 그 줄 앞에서 멈칫한다. 새로 온 사람이 비슷한 땜질을 따라 하는 일도 생긴다.
이유를 못 찾은 코드는 되돌릴 수 있는 방식으로 지운다. 지운 변경만 따로 배포하면 문제가 생겼을 때 그 커밋 하나만 되돌리면 된다. 기능 플래그 뒤에 숨겨서 일부 사용자에게만 먼저 꺼 보는 방법도 있다. magic 스위치를 넘기기 전에 컴퓨터를 다시 살릴 방법부터 확인해 두는 셈이다.
울타리를 세울 때
이 이야기의 반대편도 있다. 지금 우리가 쓰는 땜질 코드도 몇 년 뒤에는 누군가에게 이유를 모르는 울타리가 된다. 그 사람이 git log 를 뒤질 수고를 덜어 주려면 이유를 코드 가까이에 남겨 두면 된다.
// 결제창 iframe 로딩 지연 대응, 이슈 #482
await new Promise((resolve) => setTimeout(resolve, 300));주석 한 줄에 무엇을 막으려는 코드인지와 근거를 적어 두면 다음 사람은 체스터턴의 숙제를 1분 만에 끝낼 수 있다. 그 이슈가 해결된 걸 확인하면 마음 편히 지울 수도 있다. 삭제 금지라고만 적힌 주석은 울타리에 자물쇠만 하나 더 거는 일이다.
다음에 쓸모없어 보이는 코드를 지우고 싶어지면 git log -S 부터 한 번 돌려 보자. 대부분은 1분 안에 이유가 나온다. 이유가 끝내 안 나오는 스위치라면 되돌릴 준비를 해 두고 넘기면 된다.