1.7똥통에서 뒹굴기
늪과 같이 복잡하고 바꾸기 어려운 코드들에 접근하는 방법
TL;DR
늪과 같은 코드들이 있다. 코드가 촘촘히 짜여있어 고치기도 어렵고, 뭔가 바꿔보려 하면 많은 에러를 뱉어낸다. 바꿔보려하면 할 수록 그 속에 더 깊이 빠져든다
이런 코드는 어떻게 접근해야할까?
똥냄새 맡기
훌륭한 코드는 한 편의 시와 같아, 안에 알아볼 수 있는 구조나 운율, 적절히 조율된 박자, 일관성과 아름다움이 있다. 어떤 코드는 반대로, 더럽고 구조적이지도 않다. 어떤 코드는 레이아웃이 엉망이라 읽기조차 어렵다. 어떤 코드는 더 사악해서, 한 부분이 바뀌면 전혀 상관없는 곳에서 에러가 발생한다.
이상한 코드를 효과적으로 다루기 위해서는, 문제 지점을 어떻게 찾고 다룰지 알아야 한다.
똥통 헤치고 나아가기
우선 진짜 늪과 같은 코드인지 아닌지 확인한다. 단지 친숙하지 않은건 아닐까? 충분히 살펴보기 전까지는 코드나 코드 작성자에 대해 경솔한 판단을 내려선 안 된다.
조잡한 코드를 일부러 짜는 사람은 거의 없다. 그 어떤 코드도 완벽하길 기대해서는 안 된다.
똥통 조사 결과
새로운 코드에 대해 mental model을 만들고 나면, 평가 기준에 따라 코드의 품질을 측정할 수 있다.
- 외부에 노출되는 API는 깔끔하고 합리적인가?
- 자료형을 잘 고르고, 변수명을 적절히 지었는가?
- 코드의 레이아웃을 정돈하여 일관성 있게 작성했는가?
- 물론 외관만으로 코드를 평가할 수는 있지만, 일반적으로 레이아웃이 안 좋으면 코드도 좋지 않다.
- 객체들의 협업 구조가 보기에 간결하고 명확한가?
- 특정 기능을 구현하는 코드 부분이 어디에 있는지 쉽게 찾을 수 있는가?
조사에 소프트웨어 고고학을 도입해, 버전 관리 도구의 로그를 확인할 수 있다. 얼마나 오래된 코드이며, 얼마나 많은 사람들이 작업했으며, 마지막으로 수정된건 언제이며...
수렁에서 일하기
늪과 같은 코드는 어떻게 공략해야할까?
- 나쁜 코드를 고쳐야만 할까?
- 현재의 문제를 해결할 최소한의 수정만 한 뒤, 달아나야 할까?
- 괴사 부위를 도려내고, 새롭게 더 나은 코드로 바꿔야할까?
해당 코드를 다루는 일에 얼마 동안 투입될지를 알 수 있다면, 어느 정도 시간과 노력을 들여야할지 정할 수 있다. 시간이 없다면 전체를 뒤엎으려고 하면 안된다!
코드가 별로일수도 있지만, 별다른 수정 없이 몇 년 동안 작동했따면 지금 수정하는 것은 적절하지 않을 수 있다. 물론 오물 위에 둥둥 떠다녀도 된다는건 아니다. 이후에 점진적으로 수정해 나가면서 코드를 개선해야 한다.
똥 치우기
Robert Martin의 '보이스카우트 규칙'에는, "캠핑을 마치고 떠날 때는 반드시 캠핑하기 전보다 더 깨끗하게 정리하고 떠나야 한다"라는 것이 있다. 이 말처럼, 오늘 광범위한 수정을 깔끔하게 모두 마칠수는 없어도 조금은 나아지게 만들 수도 있다.
간단한 수정이라도 좋다. 코드 외관을 정리하고, 변수명을 적절히 바꾸고, 복잡한 브랜치를 간단하게 바꿔라. 거대한 함수는 더 작으면서도 적절히 명명된 작은 함수들로 나누라.
주기적으로 코드 일부라도 확인하고 약간씩 나아지게 만든다면, 머지않아 좋은 결과물을 만들 수 있다.
수정하기
가장 중요한 조언은 다음과 같다:
코드 수정은 천천히, 신중하게 하라. 한 번에 하나씩 수정하라.
실질적으로는 다음의 방법을 쓴다:
- 기능을 변경하면서 코드의 레이아웃을 바꾸지 말라. 꼭 필요한 경우, 레이아웃을 변경하고 해당 코드를 소스 관리 도구에 커밋한다. 이후 기능 변경을 한다.
- 수정으로 인해 기존 기능에 문제가 생기지 않음을 보장할 수 있는 모든 수단을 사용해라. 신뢰할만한 자동화 도구를 사용하고, 만약 안된다면 변경 사항들을 충분히 세심하게 검토하고 검증한다.
- 적절한 단위 테스트들로 코드를 충분히 둘러싼다.
- 코드를 감싼 API를 수정하되 내부 로직을 직접 수정하지 말라. 적절한 명칭과 매개변수 타입, 순서를 가지도록 수정하라.
코드 변경을 잘 할 수 있다는 용기를 가져라. 소스 관리 도구가 있으니 안전하다. 실수하더라도 금세 되돌리고 다시 수정할 수 있고, 이 과정은 낭비가 아니다. 이를 통해 반드시 코드에 대해 배울 수 있는 것이 있다.
나쁜 코드? 나쁜 프로그래머?
코드에 비난을 퍼붓는건 도움이 되지 않는다. 가능한 코드를 변경할 기회를 즐겨라. 똥 덩어리에 적절한 구조와 청결함을 불어넣는 작업은 꽤 보람이 있다.
수정하면서 자신의 태도도 확인하라. 어쩌면 당신은 원 저자보다 더 잘 알고 있을 수 있지만, 과연 언제나 그럴까?
🥸 생각해보기
1. 어째서 코드는 자주 똥 덩어리가 되는 것일까?
개인적으로는 처음에 아주 명확하게 어떻게 구현할지에 대한 밑그림을 그리지 않은 경우 만들다가 하나 더 추가하고, 만들다가 생각지 못한 걸 고치고, 바꾸고 하는 과정에서 코드가 복잡해진 것 같다. 어떤 기능을 구현하겠다고 마음 먹었을 때 머리 또는 노트 위에 상당히 구체적인 그림을 그릴 수 있어야(물론 그 와중에 생략해도 무방한 것들은 깔끔하게 생략하는 것도 능력일 것이다) 코드가 선명하고 읽기 쉽게 작성되는 것 같다.
2. 어떻게 하면 작업을 시작하는 단계부터 이런 일을 방지할 수 있을까? 가능하기는 한가?
처음하는 일인데 너무 생각만 하다가 구현을 할 시간을 충분히 확보하지 못할까봐 무작정 덤벼드는 경우가 많다. 생각해보면, 시작하는 단계부터 이런 일을 완벽히 방지하려고 모든 예상 가능한 추가될 파일들과 타입들을 만드는 것은 불필요하게 프로젝트를 복잡하게 만들 우려가 있다고 생각한다. 따라서, 우선 가능하지 않을 수도 있으며 가능하더라도 바람직하지 않을 가능성도 크다고 생각한다.
3. 코드 레이아웃 변경과 코드 기능 변경을 분리하는 것의 장점은 무엇인가?
커밋을 따로 구분함으로, 최종 버전이 뭔가 기능 오류 또는 코드 레이아웃에 변경을 필요로 할 때 해당 커밋만 수정할 수 있다. 분리하지 않을 경우 레이아웃 변경과 코드 기능 변경과 관련된 코드들이 모두 섞여 있어 해당 부분만 고치기가 어려워진다.
4. 보기 싫은 코드에 얼마나 자주 맞닥뜨렸는가? 정말 그 코드가 심각했던 경우가 잦았는가? 아니면 그저 입맛에 안 맞았던 것은 아닌가?
남이 쓴 코드 뿐 아니라 내가 쓴 코드들도 보기 싫은 코드가 많이 있었는데, 대개는 당시 그 기능을 처음으로 구현해볼때 이리 저리 시도하면서 덕지덕지 붙이느라 발생한 코드들이었다. 이 코드들은 대부분 정말 심각해서 아예 밑그림을 다시 그리고 설계했어야 했다. 가장 기억에 남는것은 부끄럽지만 내가 구현했던, 우리 회사 프로덕트의 AudioPlayer라는 클래스였는데 wavesurfer라는 라이브러리에 익숙하지 않았고 web audio api에 대한 이해도 높지 않아서 타이밍을 맞추거나 이전 트랙/다음 트랙 재생 등을 엉터리로 구현해두었다. 이 코드들은 일단은 작동했지만, 너무 복잡했던 나머지 이렇게 하면 안되고 저렇게 하면 안되고 하는 케이스들이 계속 발견되었다.
따라서 아예 해당 커밋을 돌리고 다시 구현해보았는데 아무래도 한번 구현하고 고치려고 했을때 어려웠던 포인트들이 기억나서 그런지 훨씬 잘 만들 수 있었고, 나중엔 동료가 해당 클래스를 다른 곳에서 쓸 일이 있어 썼는데 필요한게 다 있었고 쉽게 옮길 수 있다는 칭찬을 들을 수 있었다.