박영록 님의 홈페이지에는 소프트웨어 개발에 있어서 강렬한 인사이트를 주는 글이 많다. 그 중 최근에 다시 읽은 두 가지 글을 정리했다. (사실 이 정리는 내가 저 글들을 더 잘 흡수하기 위한 것에 불과하다. 원문이 훨씬 좋으니 관심이 생기신 분은 그냥 원문을 읽으시길 바란다.)


프로그래머에게 필요한 피드백

프로그래머는 어떤 피드백을 받아야 전문가로 발전하는 데 도움이 될까?

  • 성과에 대한 명확한 지표가 필요하다. 참여한 프로젝트가 얼마나 돈을 벌었는지, 얼마나 많은 사람에게 돈을 주었는지 등. 단, 그 안에서 내가 한 역할은 무엇이었는지도 함께 생각해야 한다.
  • 이러한 최종 성과보다 더 직접적인 피드백은 clean code that works다. 우선 너무나 당연한 것이지만, 제대로 돌아가게 하는 게 첫번째다. 내가 이 프로젝트에서 필요한 기능을 제대로 만들어내고 있는가? 이게 전제다.
  • 그다음은 clean code다. 여기서 clean code는 목표가 아니라 수단이다. 일단 잘 돌아간다는 전제 하에, clean code는 더 빨리 개발하기 위한 수단이다. 더 빨리 개발을 해낼 수 없다면 clean code는 무의미하다. 거꾸로, clean하지 않을수록 컨텍스트를 쉽게 잃어버리고 점점 개발속도가 느려지기 때문에, 스케일이 커질수록 clean code 없이 개발 속도를 낼 수는 없다. 즉 개발 속도가 빠르다는 것은 working하는 프로젝트를 clean하게 잘 개발한다는 것과 같다.
  • 따라서, 돌아가게 만든다는 전제 하에 얼마나 빨리 개발할 수 있느냐가 개발자 능력에 가장 중요한 척도다. 이걸 ‘정확’하진 않아도 ‘정량’적으로는 뽑아내야 한다. 예를 들면 비슷한 기능을 만들어내는데 얼마나 걸렸는가? 예전에는 3주, 그 다음은 3일, 최근에는 하루. 디테일, 언어, 프레임워크, 환경 등이 다 다르더라도 비슷한 부분은 뽑아내서 비교할 수 있다. 반복 효과를 감안하더라도 대략적인 측정이 가능하다. 이에 대한 피드백을 계속 받아야 전문가로 성장할 수 있다.
  • 그런데 ‘돌아간다’, 즉 품질에 대한 기준은 사람마다 다르다. 어느 정도 품질을 기준으로 빨리 해야 할까?
  • 영록님의 주장은 품질과 상관 없이 무조건 빨리 해야 한다는 것이다. 중요한 건 최종 배포 때 버그가 없는 것이지 매 순간 버그가 없는 게 아니다. 빨리 개발하면 빨리 버그를 찾아서 빨리 고칠 수 있다. 품질을 높이기 위해 신경쓰면 고객에게 기능이 전달되는 게 늦어진다. 차라리 버그가 있는 상태에서 고객과 소통하는 이득이 더 크다. 기능이 고객에게 맞는지 아닌지를 볼 수 있기 때문이다.
  • 품질을 깎아 먹은 결과로 전체 프로젝트가 더 느려지는 건 당연히 안 된다. 개별 기능 구현 뿐 아니라 프로젝트 전체가 빨리 끝나야 한다. TDD, 리팩터링 등 각종 기법의 모든 목표는 생산성, 즉 더 빨리 프로젝트를 완료하는 것이다. clean code의 극한을 추구하느라 느리게 개발하는 건 용납될 수 없다.
  • 정리하면, 프로그래머에게 필요한 피드백은 세 가지다. 1) 내가 참여한 프로젝트가 성공했는가. 2) 내가 개발한 기능은 제대로 동작하는가. 3) 나의 개발 속도는 얼마나 빠른가. 성장을 위해 가장 중요한 건 세 번째다. 물론, 여기에 성장 의지, 피드백에서 알아낸 상관 관계에서 인과 관계를 뽑아낼 분석력 등도 필요하다. 피드백은 전문가로 가는 길의 첫걸음 정도로 생각하면 된다.

Quick And Dirty

위 글의 다음 시리즈 글. 스타트업은 어떻게 quick & dirty를 해야 하는가?

  • 먼저, clean code에 도달하는 가장 훌륭한 방법인 TDD는 다음과 같다.

    1. 제대로 동작하는지 확인할 수 있는 테스트를 작성한다.
    2. 실행 가능하게 만든다. 다른 무엇보다도 중요한 것은 빨리 초록 막대를 보는 것이다. 빨리 초록 막대를 보는 것은 모든 죄를 사해준다. 하지만 아주 잠시동안만이다.
    3. 올바르게 만든다(리팩토링). 이제 시스템이 작동하므로 직전에 저질렀던 죄악을 수습하자.
  • 린 스타트업은 TDD를 조금 더 큰 스케일로 하는 것이다.
    1. 가설을 수립하고, 가설을 확인할 수 있는 지표를 설정한다.
    2. 가설을 검증할 수 있는 MVP를 만든다. quick & dirty!
    3. 가설이 검증되었다면 완성도를 높여간다.
    4. 가설 검증 결과가 틀린 것으로 나온다면 새로운 가설을 수립한다.
  • 문제는 2와 3 사이에 있다. 가설을 검증하는 MVP를 빠르게 만들었으면 빠르게 만들었으면 그 코드를 리팩토링해야 한다. 리팩토링을 하지 않으면 모범 답안을 보고 달리는 추격자에게 자리를 내주게 된다. MVP까지는 quick & dirty로 빠르게 만들 수 있어도 완성품을 만드는 것은 clean code 없이 속도를 낼 수 없다. 윗 글에서도 말했듯, 스케일이 커지면 clean하지 않으면 속도가 점점 느려진다.
  • 여기서 CEO가 엔지니어링 마인드를 가지고 있지 않다면 2.5에 있는 리팩토링의 필요성을 이해하지 못한다. CEO는 quick & dirty로 가설을 검증해냈기 때문에 앞으로도 쭉 quick & dirty로 해나가면 되고, 앞으로도 계속 같은 속도로 움직일 수 있을 거라고 생각한다. 설령 말로는 리팩토링이 필요하다는 개발자의 말에 동의하더라도 그걸 할 시간을 충분히 주지 않는다.
  • 그런데 어차피 대부분의 스타트업은 3으로 넘어가지 못한다. 시장에서 proof of concept를 못하고 접는 스타트업이 얼마나 많은가. 즉 대부분의 스타트업에서 개발자가 고민해야 할 단계는 2단계다. “MVP를 어떻게 quick & dirty로 개발할 것인가”이다.
  • MVP, Minumum Viable Product에서 중요한 건 Minimum이다. MVP는 충분히 작아야 한다. MVP가 충분히 작다면, quick & dirty로 매우 빨리 만들어도 그 과정에서 기술 부채는 별로 쌓이지 않고, 가설 검증이 되면 바로 갚을 수 있다.
  • 그러나 대부분의 스타트업은 Minimum을 못한다. 오히려 기능이 부족해서 고객이 안 온다고 생각하며 Maximum을 만드려고 한다. 그렇게 제품이 점점 비대해지면 quick & dirty로 인한 기술 부채는 감당할 수 없을 만큼 쌓여간다. 이러다 보면 속도가 점점 느려져서 어느 순간 아무 것도 할 수 없는 상태가 된다. 그리고 어떻게 가설 검증이 되었더라도, 그 사이 quick & dirty로 만든 제품이 이미 너무 비대하다면 아예 리팩토링에도 손을 못 대는 (즉 2.5단계로 넘어가지 못하는) 상황이 온다.
  • 따라서 스타트업은 MVP를 극도로 절제하며 만들어야 한다. 가설의 성공/실패 여부가 가려지지 않았을 때 새로 기능을 추가하는 것이 결과적으로 성공 확률을 깎아먹는다. 기술 부채를 통제할 수 있는 선에서 MVP를 만들어야 한다.
  • 그러면 Minumum을 잘 설정했다 치고, quick & dirty로 어떻게 개발해야 할까? 여기서 제시하는 방법들은 clean code의 궁극을 추구하는 게 아니라, 기술 부채로 파산하는 위험을 아주 작은 노력으로 막는 것이다.
    • 추상화하지 말라. 잘못된 추상화는 코드를 재앙으로 이끈다. 초기에 좋은 추상을 발견할 시간이 부족하다면 추상화를 하지 말고 미루라.
    • 메소드를 추출하지 말라. 함수도 추상의 일부다. 추출함으로써 상태를 더 관리해야 한다거나 이해할 컨텍스트가 넓어진다거나 할 수 있다. 잘 추출되지 않은 함수가 있는 것보다 차라리 한 군데 모여있는 1000줄짜리 메서드가 리팩토링하기 더 좋다.
    • 상속 계층을 만들지 말라. 다형성에 대한 완벽한 이해가 없다면 좋은 상속 구조를 만들어내기 어렵다. 스스로 OOP를 잘한다고 생각하지 않는다면 상속을 차라리 전혀 쓰지 말라.
    • 중복을 한 파일에 몰아라. 리팩토링은 중복을 제거하고 의도를 드러내는 것이다. 그런데 중복을 제거하려면 추상화를 하게 되는데, 좋은 추상화를 하기 어렵다. 그러니 일단 한 군데 중복을 몰아넣어라. 여러 곳에 걸쳐 퍼진 중복은 나중에 리팩토링하기 어렵다.
    • Cyclomatic Complexity를 줄여라. 한 마디로 말하면 분기의 depth를 늘리지 않는 것이다. 예를 들면 guard clause를 활용해서 if를 flatten하는 것. 그리고 중첩된 if는 중복이 있더라도 풀어두는 게 좋다. 이해하기 쉬운 중복은 리팩토링하기 쉽다.
    • 이름 규칙을 정해라. quick & dirty는 리팩토링도 대충 하기 때문에 네이밍을 열심히 하진 않아도 되지만 규칙은 잘 정해둬야 한다. CSS class 이름, 태그 id 등 규칙을 잘 지켜놓으면 협업할 때 이해하기도 쉽고 나중에 리팩토링하기 쉽다.
    • 안 쓰는 코드와 파일을 삭제하라. quick & dirty로 만들어진 코드는 어차피 나중에 레거시가 될 운명을 타고났다. 한 눈에 들어오는 파일 갯수도 유지보수에 큰 영향을 미친다. 쓰지 않는 코드는 주석처리하지 말고 삭제하라.
    • 주석은 되도록 쓰지 말라. 코드 분석에 도움될 정도의 주석을 쓸 시간에 리팩토링을 하든가 구현을 더 빨리 하라. 주석은 코드만큼 실시간으로 업데이트되지 않기도 하고, 좋은 주석을 쓰기도 어렵다. 비정상적인 방법으로 구현했거나, why를 남겨야 할 때 주석을 남겨라.
    • 코드 외의 기술적 사항을 문서로 남겨라. 각종 설정 파일, 배포 방법 등의 정보가 실종되면 레거시를 더이상 업데이트할 수 없는 일이 생긴다. 이런 것도 모두 코드로 간주해서 버전관리 및 자동화를 하거나, 그 시간도 아깝다면 문서로라도 남겨놔야 한다.
    • 적정 기술을 사용하라. 최신 기술 일부러 쓰려 하지 말고, 생산성이 높고 검증된 프레임워크를 사용하라. 눈앞의 과제에 대한 최선의 해결책을 찾으려고 하다 보면 필요한 신기술은 자연스럽게 익히게 된다. 따라가려고 하지 말고, 나중에 찾을 수 있는 정도로만 변화의 흐름에 귀만 열어두라.
    • 동일 목적의 프레임워크/라이브러리를 여러 개 쓰지 말라. 지금 쓰는 것이 마음에 안 들어서 바꾸는 것도 좋고, 점차적으로 갈아엎는 것도 좋지만, 그 공존 상태는 일시적이어야 한다. 그렇지 않으면 나중에 변경하려고 할 때 작업량이 몇 배로 늘어난다.
  • 어쨌든 스타트업은 quick & dirty를 해야 한다. ‘아무도 쓰지 않는 훌륭한 제품’은 결국 쓰레기다. 기술 부채 만드는 것을 두려워하면 안 되지만, 그 부채는 감당할 수 있는 수준으로, 파산하지 않게 유지해야 한다. 너무 좋은 코드도 너무 나쁜 코드도 스타트업에게는 좋지 않다. 적정 기술, 적정 품질을 생각하며 일하는 엔지니어가 되자.

언젠가 ‘어떤 개발자가 훌륭한 개발자인가?’라는 주제로 토론을 한 적이 있다. 그 때 내 의견은 대략 “고객에게 가치 있는 제품을 빨리 전달할 수 있는 사람”이었던 것 같다. 이 기준 자체는 영록님의 의견과 상당히 비슷하다. 그런데, 나는 ‘내가 더 좋은 개발자가 되어가고 있는가?’를 고민하고, 측정하며, 나 스스로에게 피드백을 주진 않았었다. 의도적 수련이라는 키워드를 접한지는 오래 됐지만 별로 의도적 수련을 하지 않고 있었던 것이다… 거참.

크게 아파서 회사에 병가를 냈다가 복귀한 이후로 1) 출퇴근 시간에 글 읽기 2) 일과 중의 지루한 디버깅 업무를 기록하고 패턴화하는 훈련 3) 퇴근 후 블로깅 세 가지를 꾸준히 하고 있다. 이로부터 삶의 만족도도 높여주고 개발자로서도 성장하고 있다고 느꼈는데, 이제는 개발자로서의 나를 정량적으로 측정해보고 피드백을 해봐야겠다.