새해도 밝았고 올해 시작을 알리는 챌린지로 클린코드를 도전했다. 아직 10장 이후에 정리하고자 하는 마음이 있지만, 일단 3주 챌린지의 후기를 남겨보려고 한다.

2~3년 전쯤에 클린코드 챌린지를 도전했다가, 장수를 넘어갈수록 피부에 와닿는 내용도 점점 줄어가고 나태함에 도중에 접었었다. 이번 챌린지는 클린코드를 다시 읽고 정리하면서, 내가 얼마나 많은 부분을 놓치고 있었는지 깨달았다. 같은 책인데 읽는 사람이 달라져 있었다. 그때는 규칙이 잔소리로 들렸는데, 이번에는 내가 겪은 고통에 대한 처방으로 읽혔다.

코드를 쓰는 것은 쉽다. 하지만 좋은 코드를 쓰는 것은 어렵다

책 전체를 두 문장으로 줄이면 이렇다.

  1. 모든 코드는 작성자의 의도를 명확히 표현해야 한다.
  2. 코드는 누구나 쉽게 읽고 이해할 수 있어야 한다.

이 두 명제를 되새기며 내가 작성한 코드를 다시 읽어보니, 생각보다 많은 부분에서 미흡함을 느꼈다. 동작은 하지만 6개월 뒤의 내가 읽으면 다시 해독해야 하는 코드가 대부분이었다. 컴파일러는 지저분한 코드도 이해하지만, 사람은 그러지 못한다. 결국 좋은 코드의 기준은 “수정하기 쉬운가”였다.

책에서 언급한 원칙들을 복기하다 보니, 두 명제를 지키는 데 더 많은 세부 원칙이 필요하다는 걸 알게 됐다. 그중 내게 특히 남은 것들을 정리한다.

남은 실천 원칙들

  • 이름이 곧 설계다. 좋은 이름을 짓는 데 드는 시간은 낭비가 아니다. 이름이 의도를 말하면 주석이 필요 없어지고, 이름이 구현을 노출하면(16장의 SerialDate처럼) 추상화가 새고 있다는 신호다. 나는 이제 변수 하나에도 잠깐 멈춰 “이게 무엇인지”를 이름에 담으려 한다.

  • 함수는 작게, 하나만. 한 함수가 한 가지 일만 하도록 쪼개면 테스트도 이름도 쉬워진다. 플래그 인수나 출력 인수가 보이면 대개 함수가 두 가지 일을 하고 있다는 자백이었다.

  • 중복은 놓친 추상화다. 복붙한 코드가 두 번째로 등장하는 순간이 정리할 시점이다. 세 번째를 기다리면 이미 늦다.

  • 테스트가 먼저다. 16장에서 가장 크게 배운 것. 리팩터링은 테스트라는 안전망 위에서만 안전하다. 커버리지 없이 구조를 바꾸는 건 회귀를 심는 일이다. 저자조차 리팩터링 중 실수했고, 테스트가 그걸 잡아냈다.

  • 냄새를 맡는 감각. 17장의 목록은 외울 대상이 아니라 감각을 기르는 도구였다. 규칙을 암기하는 게 아니라, 코드에서 “뭔가 이상하다”를 빨리 느끼고 그 느낌에 이름(G5, N7 같은)을 붙일 수 있게 되는 것이 목표다.

  • 경계 조건은 한 곳에서. 흩어진 경계 계산은 반드시 어딘가 하나가 틀린다. +1, -1 같은 조정이 여러 곳에 복제돼 있으면 그 자체가 버그의 서식지였다.

이 원칙들은 서로 독립적이지 않다. 이름을 잘 지으면 함수가 작아지고, 함수가 작아지면 중복이 눈에 띄고, 중복을 없애면 테스트가 쉬워진다. 하나를 당기면 나머지가 따라온다는 걸 정리하면서 느꼈다.

꾸준히 읽고, 쓰고, 고치고, 반복하자

개발을 시작하고 점점 주니어 딱지가 떼어지기 시작하는데, 앎에 대한 척도는 아직 형편없다고 생각한다. 이번 챌린지가 알려준 건 정답이 아니라 태도였다. 깨끗한 코드는 한 번의 규칙 적용으로 완성되는 게 아니라, 체크아웃했을 때보다 조금 더 깨끗하게 체크인하는 작은 규율을 매일 반복하는 데서 나온다. 보이스카우트 규칙이 결국 이 책의 결론이었다.

개발 관련 패러다임을 이제라도 깨닫고, 더 나은 코드를 작성하기 위해 노력해야겠다. 완벽한 코드를 한 번에 쓰겠다는 욕심보다, 어제보다 나은 코드를 오늘 커밋하겠다는 마음으로.

다음 챌린지에서는 여기서 정리한 원칙들을 실제 리뷰와 리팩터링에 적용해 보며, 읽은 것을 아는 것에서 하는 것으로 옮겨보려 한다. 책은 읽었을 때가 아니라 손이 그 규율을 기억할 때 비로소 내 것이 된다고 믿는다.

img.png