이 글은 Martin Tournoij의 글 Easy means easy to debug를 번역한 글입니다. 다수의 의역이 포함되어 있으며, 원문을 읽고 싶으신 분들은 Easy means easy to debug을 참조하시기 바랍니다. 또한 이 문서는 cc-by 4.0 라이센스를 준수합니다.

어떤 프레임워크나 라이브러리, 도구가 "쉽다"는 건 무슨 의미일까? 여기엔 다양한 의견이 있겠지만 나는 사용하기 쉬운 도구는 디버깅하기 쉬운 도구 라고 생각한다. 사람들이 프로그램, 프레임워크, 라이브러리, 파일 포맷 등이 사용하기 편하다고 말할 땐, "보세요. 이 정도 노력으로 이 정도 일을 할 수 있어요. 정말 쉽지 않나요?" 같은 식으로 말하곤 한다. 하지만 그것만으론 충분하지 않다.

코드는 한 번 작성하면 언제나 디버깅 사이클을 거치게 되어 있다. 이는 단순히 코드에서 버그를 찾아내는 것만을 말하진 않는다. 여기에는 코드를 읽고 이해하는 모든 과정이 포함된다. 결국 디버깅을 하려면 코드 전체를 이해해야 한다. 그러니 "디버깅하기 쉽다"는 말에는 "이해하기 쉽다"는 의미도 포함된다.

사용성 측면의 추상화는 가끔 그 코드가 정확히 어떤 동작을 수행하는지 이해하기 어렵게 만든다. 물론 이는 어떤 상황에선 나쁘지 않은 타협점(tradeoff)일 수 있다. 하지만 나라면 코드를 나중에 더 이해하기 쉽고 디버깅하기 쉬운 방향으로 고치기 위해 더 많은 노력을 쏟을 것 같다. 나중에 돌이켜보면 이런 노력이 그만한 값어치를 했다는 걸 깨닫을 것이다.

단순함 그 자체가 디버깅하기 쉬운 프로그램을 만드는 건 아니지만 가장 중요한 부분인 것도 사실이다. 질 좋은 문서를 갖추는건 큰 도움이 되지만 좋은 문서를 이용할 수 있는 상황이 흔하지는 않다.

코드를 이해하기 쉽고 디버깅하기 쉬운 방향으로 고쳤을 때의 이점은 분명하다. 디버깅하기 어려운 프로그램은 버그의 개수가 늘어나지 않더라도 계속 더 이해하기 어려워 진다. 이 때문에 프로그래머는 디버깅에 더 많은 시간을 쏟게 되고, 결국 프로그램은 더 많은 버그를 가지게 될 것이기 때문이다.

내 관찰에 따르면 회사는 고치기 어려운 버그에 시간을 들이는 일을 효용이 떨어지는 일로 보는 경향(not considered good Return Of Investment)이 있다. 그리고 오픈소스 프로젝트에선 대체적으로 소수의 핵심 인물을 제외하곤 다수가 일회성이나 작은 수정에 불과한 커밋에만 집중한다.

새로운 사실은 아니다. Brian W. Kernighan과 P. J. Plauger가 1974년 발행한 The Elements of Programming Style 에 따르면:

모두가 프로그램을 처음 작성하는 것보다 디버깅하는 것이 두 배는 더 힘들다는 사실을 알고 있다. 그럼에도 처음 코드를 작성할 때 온갖 영리한 추상화를 집어넣는다면(as clever as you can be) 나중에 어떻게 디버깅을 할 것인가?

온갖 영리한 추상화를 집어넣은 코드는 디버깅하기 어려운 코드가 된다. 이제 몇 가지 예시를 들텐데, 내 주장은 도구를 사용하는 것 자체가 나쁘다는 것은 아니다. 오히려 아래 예시들은 "사용하기 쉬운" 도구와 "디버깅하기 쉬운" 도구 간에 벌어지는 타혐점(tradeoff)을 분명하게 보여주기 위한 것들이다.

  • ORM 라이브러리(: 데이터베이스 쿼리, 마이그레이션 등을 쉽게 사용하기 위한 라이브러리)를 사용하면 데이터베이스 쿼리를 쉽게 사용할 수 있지만 그와 관련해서 문제가 생겼을 때 이를 고치는 건 그 도구를 사용하지 않을 때보다 어렵다.
  • 다수의 테스트 라이브러리가 디버깅을 어렵게 만들기도 한다. 루비의 rspec이 좋은 예시인데, 내가 만약 설정을 잘못하게 되면, 그 후에 무엇이 잘못되었는지 파악하려면 꽤 많은 시간을 들여야한다. 이는 에러메시지가 이해하기 어렵기 때문이다. 자세한 것은 Testing isn’t everything포스팅을 읽어보기 바란다.
  • 자바스크립트 프레임워크의 동작 전체를 이해하는 건 어려운 일이다. 영리한 상태 관리는 굉장한 편의성을 제공하고 그 자체로 훌륭한 도구지만, 상태(state)가 예상한 방향대로 변화하지 않으면 관련된 스택오버플로우 질문이나 깃허브 이슈가 있기를 바라면서 한참을 해매야한다. 이런 라이브러리들이 상태 관리를 쉽게 하게끔 도와주는 것은 사실이고 이런 라이브러리를 사용하는게 나쁘다는 건 아니다. 하지만 내가 보기엔 이 도구들은 "사용의 편의성"에만 너무 집중한 나머지 "디버깅의 편의성"을 잃어버린 것 같다.
  • 도커(Docker)는 사용성을 높여주는 훌륭한 도구지만,
    ERROR: for elasticsearch  Cannot start service elasticsearch:
    oci runtime error: container_linux.go:247: starting container process caused "process_linux.go:258:
    applying cgroup configuration for process caused \"failed to write 898 to cgroup.procs: write
    /sys/fs/cgroup/cpu,cpuacct/docker/b13312efc203e518e3864fc3f9d00b4561168ebd4d9aad590cc56da610b8dd0e/cgroup.procs:
    invalid argument\""
    이런 에러메시지나
    ERROR: for elasticsearch  Cannot start service elasticsearch: EOF
    이런 메시지가 나오면 그저 막막할 수밖에 없다.
  • Systemd는 SysV init.d 스크립트보다 쉽다.
    Lennart Poettering의 책 systemd myths 에서 그는 systemd가 왜 사용하기 쉬운지를 설명하기 위해 아래와 같이 말했다.

    Systemd는 SysV init.d 스크립트보다 쉬운 이유는 쉘 스크립트를 작성하는 것보다 systemd 유닛 파일을 작성하는게 더 쉽기 때문입니다.

내가 그의 의견 전체에 동의하지 않는 것은 아니다. 일반적으로 쉘 스크립트는 길고 장황해서 이해하기 어렵지만 - the shell scripting trap - 그렇다고 하더라도 사용자가 유닛 파일을 작성하기 쉽게 만들려면 상당한 레벨의 추상화가 필요하며,이는 systemd 자체는 그보다 훨씬 복잡하다는 것을 의미한다. 또 사용자도 이러한 복잡성 때문에 불편을 겪을 수 있다. 내가 겪은 이슈해결책를 한 번 살펴보길 바란다. 결코 이해하기 쉽다고는 말할 수 없을 것이다.


사견:

프로그래밍을 배우다보면 개발의 목적이 "유용한 것을 만드는 것"에서 "멋진 도구를 사용해보는 것"으로 바뀔 때가 있습니다. 소위 말하는 라이브러리 덕후가 되는 것이죠.

물론 적은 코드로 많은 일을 할 수 있는 도구는 분명 멋지지만 그 도구의 디버깅 경험(User Experience처럼 개발자에게는 디버깅 경험이 있다고 생각합니다.)이 나쁘다면 훌륭한 도구가 아니라고 생각합니다. 그러다보니 이러니 저러니 해도 많은 사람들이 사용하는 다루기 쉬운 도구로 계속 돌아오는 것 같아요. 이런 저런 불평하다가도 다른 도구를 사용하면서 스택오버플로우 질문 뒤질 것 생각하면...

개발자라면 누구나 원작자처럼 디버깅하기 어려운 도구때문에 곤란을 겪은 경험이 있을 것이라고 생각합니다. 저한테는 TypescriptRedux 혹은 Angularjs가 그랬죠. 디버깅하기 어렵다고 그런 도구들을 버릴 수 있는 건 아니지만 언제나 더 디버깅하기 쉬운 도구를 찾게 되는 것 같아요.

재밌게 읽으셨다면 원작자분의 원문도 읽어보시기 바랍니다. 좋은 인사이트가 담겨있다고 생각해요. 😀