본문 바로가기

카테고리 없음

프로그래머가 되는 방법: 짧고 폭넓고 개인적인 요약.

How to be a Programmer: A Short, Comprehensive, and Personal Summary

프로그래머가 되는 방법: 짧고 폭넓고 개인적인 요약.


번역 처로 강창기

Copyright ⓒ 2002, 2003 Robert L. Read

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with one Invariant Section being 'History (As of May, 2003)', no Front-Cover Texts, and one Back-Cover Text: 'The original version of this document was written by Robert L. Read without renumeration and dedicated to the programmers of Hire.com.' A copy of the license is included in the section entitled 'GNU Free Documentation License'.


1 도입 

좋은 프로그래머가 되는 것은 어렵고도 고상한 일이다. 소프트웨어 프로젝트의 공동 비전을 현실화하려고 할 때 가장 어려운 부분은 함께 일하는 개발자들과 고객들을 상대하는 일이다. 컴퓨터 프로그램을 짜는 것은 중요한 일이고 지식과 기능이 많이 드는 일이다. 하지만 그것은 좋은 프로그래머가 고객 및 자기가 크고 작게 책임을 지고 있는 수많은 동료들을 만족시키는 소프트웨어 시스템을 만들기 위해 해야 하는 다른 모든 일들에 비교해 볼 때 정말 어린아이 장난과 같다. 나는 내가 스물 한 살이었을 때 누군가가 나에게 설명해 주길 바랐던 것들을 가능한 한 간결하게 요약하려고 했다.

이것은 매우 주관적이며, 따라서 이 글은 개인적이고 다소 고집스럽게 보일 수밖에 없다. 이 글은 프로그래머가 일하면서 맞부딪치기 아주 쉬운 문제들에 한정되어 있다. 이런 문제들과 이에 대한 해결책은 사람 사는 데서 흔히 볼 수 있기 때문에 이 글이 설교처럼 보일 수도 있다. 그럼에도 불구하고 이 글이 유용하게 쓰이길 바란다.

컴퓨터 프로그래밍은 여러 강좌를 통해 배울 수 있다. The Pragmatic Programmer <Prag99>, Code Complete <CodeC93>, Rapid Development <RDev96>, Extreme Programming Explained <XP99> 등의 훌륭한 책을 통해 컴퓨터 프로그래밍에 대해 배우고, 좋은 프로그래머란 무엇인가에 대한 다양한 논점들을 알게 된다. 폴 그레이엄(Paul Graham) <PGSite>과 에릭 레이먼드(Eric Raymond) <Hacker>의 글은 이 글을 읽기 전이나 읽는 도중에 꼭 읽어 보아야 한다. 이 글은 이상의 훌륭한 글들과 달리 사회 활동의 문제를 강조하고 있으며 내가 보기에 꼭 필요하다고 생각하는 모든 기능들을 폭넓게 요약하고 있다.

이 글에서 "상사"는 나에게 프로젝트를 배정해 주는 사람을 의미한다. 사업, 회사, 부족(tribe)이라고 할 때, 사업이 돈을 버는 것, 회사가 현시대의 일터, 부족이 충성심을 공유하는 사람들이라는 뜻을 내포하는 것 외에는 모두 같은 뜻으로 사용했다.

우리 부족에 온 것을 환영한다.

2 초보자 

2.1 개인적 기능들 

2.1.1 디버그 배우기 

디버깅은 프로그래머의 기본이다. 디버그란 말의 처음 뜻은 오류를 제거하는 것이지만, 더 중요한 것은 프로그램이 실행될 때 그것을 상세히 검사하는 일이다. 효과적으로 디버그 할 줄 모르는 프로그래머는 앞을 못 보는 것과 같다.

이상주의자라면 설계, 분석, 복잡도 이론 등등이 더 기본적인 것이라고 생각할 것이다. 하지만 이들은 현업의 프로그래머가 아니다. 현업의 프로그래머는 이상적인 세계에서 살고 있지 않다. 완벽한 프로그래머라 해도, 그는 대형 소프트웨어 회사, GNU 등의 조직, 자기 동료들이 만든 코드들에 둘러싸여 있고 그것을 가지고 작업해야 한다. 이 코드들은 대부분 불완전하며 불완전하게 문서화되어 있다. 어떤 코드가 실행될 때 그것을 꿰뚫어 볼 수 있는 능력이 없다면 사소한 문제에도 대책 없이 나가떨어질 수밖에 없다. 이러한 투시력은 실험해 보는 것을 통해 얻어지며, 그것이 바로 디버깅이다.

디버깅은 프로그램의 실행에 대한 것이지 프로그램 자체에 대한 것이 아니다. 대형 소프트웨어 회사에서 프로그램을 구입했다면 보통은 프로그램을 들여다볼 수 없다. 하지만 그 코드가 문서대로 동작하지 않거나 (컴퓨터가 멈춰 버리는 것은 아주 흔하고 극적인 예이다) 문서에 원하는 내용이 없는 일은 항상 일어난다. 더 흔한 것은, 오류가 생겨서 자기가 짠 코드를 검사하는데 어떻게 그런 오류가 생길 수 있는지 전혀 실마리를 잡을 수 없는 경우이다. 당연히 이것은 그 프로그래머가 짐작하고 있는 것 중 어떤 것이 잘못됐거나, 예상하지 못했던 상황이 발생하기 때문이다. 가끔은 소스 코드 응시하기 마법으로 문제를 해결할 수 있다. 하지만 그 마법이 통하지 않을 때는 디버그를 해야 한다.

프로그램의 실행에 대한 투시력을 얻기 위해서는 코드를 실행하면서 무엇인가를 관찰해야 한다. 어떤 경우에는 그것이 화면에 나타나서 눈으로 볼 수도 있지만, 다른 많은 경우에는 코드 내의 변수의 상태, 현재 실행되고 있는 코드의 줄 수, 복잡한 자료 구조에서 어떤 검증 조건(assertion)이 계속 유지되는가의 여부 등과 같이 눈에 보이지 않는다. 이런 숨겨진 것들은 드러나야 한다. 실행되고 있는 프로그램의 내부를 들여다보기 위해 널리 쓰이는 방법을 다음과 같이 분류할 수 있다.
  • 디버깅 도구 이용
  • 프린트 줄 넣기(printlining) -- 프로그램을 임시로 고치는 것, 특히 필요한 정보를 프린트하는 줄을 추가하는 것
  • 로그 기록 -- 로그 파일의 형태로 프로그램을 들여다볼 수 있는 영구적인 창을 만드는 것 

디버깅 도구는 제대로 동작한다면 훌륭한 것이지만, 그 다음의 두 가지 방법이 더욱 더 중요하다. 디버깅 도구는 종종 언어의 발달을 따라가지 못하기 때문에 어떤 시점이 되면 쓸모 없게 될 수 있다. 그리고 디버깅 도구 자체가 프로그램의 수행 방식을 미묘하게 변화시킬 수도 있기 때문에 모든 경우에 유효한 것이 아니다. 끝으로, 대규모 자료 구조에 대한 검증 조건을 검사하는 것과 같은 디버깅의 경우에는 어떻든 간에 코드를 새로 짜야 한다. 디버깅 도구가 안정적이라면 그것의 사용 방법을 아는 것이 좋은 일이겠지만, 그 다음의 두 가지 방법을 쓸 줄 아는 것은 필수불가결한 일이다.

어떤 초보자는 코드를 고치고 실행하는 일을 반복해야 하는 디버깅에 대한 두려움이 잠재의식 속에 있는 것 같다. 이해할 만한 일이다. 이것은 처음 외과 수술을 하는 것과 비슷해 보인다. 하지만 초보자들은 코드가 쌩쌩 돌아가게 하기 위해 여기저기 찔러 보는 것에 익숙해져야 한다. 그들은 코드를 가지고 실험해 보는 것에 익숙해져야 하고, 자기가 코드를 가지고 무엇을 하더라도 그것이 문제를 악화시키지 않는다는 것을 배워야 한다. 이 소심한 사람들의 교사나 사수라면, 어떻게 해야 하는지 친절하게 알려 주고 손이라도 잡아 이끌면서 그 두려움을 극복할 수 있도록 도와 주라. 그 두려움 때문에 좋은 프로그래머가 될 수 있는 사람들도 아슬아슬하게 시작했다가 포기하는 경우가 많다.

2.1.2 문제 공간을 나눠서 디버그 하는 방법 

디버깅은 수수께끼에서 출발하기 때문에 재미있다. 이렇게 디버그 하면 저렇게 될 것이라고 생각하지만 실제로는 다른 결과가 생긴다. 디버깅은 만만한 일이 아니다. 여기에서 무슨 사례를 제시하든 그것은 실제 상황에 비하면 상당히 부자연스운 것이 될 것이다. 디버깅은 창의력과 독창성이 필요하다. 디버깅에 한 가지 열쇠가 있다면 그것은 수수께끼에 대한 분할 정복 기법(divide and conquer technique)을 사용하는 것이다.

예를 들어 열 가지 일을 차례로 하는 프로그램을 만들었다고 하자. 그런데 실행해 보니 멈춰 버렸다. 멈추도록 프로그램하지 않았는데 말이다. 이제 "프로그램이 멈춘다"는 수수께끼가 생긴 것이다. 출력된 결과를 보면 처음 7번까지는 제대로 실행된 것을 알 수 있다. 나머지 세 가지가 출력 결과에서 안 보인다. 이제 우리의 수수께끼는 "프로그램이 8번이나 9번이나 10번에서 멈췄다"로 줄어들었다.

그럼 프로그램이 어디에서 멈췄는지 알아볼 수 있는 실험을 설계할 수 있을까? 물론이다. 디버거를 쓸 수도 있고 8번과 9번 다음에 프린트 줄을 넣을 수도 있다. (물론 사용하는 언어에 적합한 다른 방법을 쓸 수도 있다.) 그리고 다시 실행해 보면 우리의 수수께끼는 "프로그램이 9번에서 멈췄다"와 같이 더 줄어든다. 어느 순간에든 수수께끼가 정확히 무엇인지 기억하는 것은 집중하는 데 도움이 된다. 여러 사람이 급하게 어떤 문제에 매달려 있을 때는 그 일이 무척 혼란스러워질 수 있다.

분할 정복이라는 열쇠는 디버깅 기법일 뿐만 아니라 알고리듬 설계 기법이기도 하다. 수수께끼의 중간을 둘로 나누는 것만으로 일 처리를 잘 할 수 있다면, 더 이상 많이 나눌 필요는 없을 것이고 디버깅도 더 빨리 끝날 것이다. 그런데 수수께끼의 중간이란 어디쯤을 말하는 것인가? 여기가 바로 창의력과 경험이 필요한 지점이다.

아직 초보인 사람에게는, 모든 오류가 존재하는 공간이 소스 코드의 모든 몇 줄뿐인 것처럼 보일 것이다. 그는 아직 프로그램의 다른 차원, 즉, 줄들이 실행되는 공간, 자료 구조, 메모리 관리, 외부 코드와 상호작용, 문제가 생길 만한 코드와 간단한 코드 등을 볼 수 있는 감각이 없다. 이런 다른 차원들은 경험이 쌓인 프로그래머에게 문제를 일으킬 수 있는 모든 것들에 대해 완벽하지는 않지만 매우 유용한 머리 속의 모형(mental model)을 형성하게 해 준다. 이런 모형을 머리 속에 갖고 있으면 수수께끼의 중간이 어디인지 효과적으로 찾는 데 도움이 된다.

문제를 일으킬 수 있는 모든 것들의 공간을 둘로 균등하게 나눴다면, 이제는 그 둘 중 어느 쪽에서 오류가 생겼을지 결정해야 한다. 수수께끼가 "프로그램을 멈추게 하는 그 줄은 이 줄이 실행되기 전에 실행됐을까, 후에 실행됐을까?"와 같이 단순한 경우에는 어느 줄이 실행되는지 관찰하기만 하면 된다. 다른 경우에는 수수께끼가 이런 식으로 분할될 것이다. "저 그래프에 잘못된 노드를 가리키는 포인터가 있거나, 그 그래프에 변수들을 추가하는 알고리듬에 문제가 있다." 이런 경우에는 분할된 수수께끼 중 어느 쪽을 버릴 것인지 결정하기 위해 그 그래프의 포인터들이 모두 정확한지 알아보는 작은 프로그램을 작성해야 할 수도 있다.

2.1.3 오류를 제거하는 방법 

나는 의도적으로 프로그램의 실행을 점검하는 행위와 오류를 고치는 행위를 구분하고 있다. 물론 디버깅은 버그를 제거하는 것을 뜻한다. 이상적으로는 코드를 완벽하게 이해하여 오류의 정체와 그것을 고칠 방법을 완벽하게 알게 되면서 "아하!" 하고 외치는 순간에 이를 수도 있다. 하지만 문서화가 잘 되어 있지 않아 그 속을 들여다 볼 수 없는 시스템들을 가지고 프로그램을 만드는 경우도 종종 있으므로 이런 일이 항상 가능한 것은 아니다. 또한 코드가 너무 복잡해서 그것을 완벽하게 이해할 수 없는 경우도 있다.

버그를 고칠 때에는 가능한 한 조금만 수정하여 버그를 고치고 싶을 것이다. 그러면서 성능 개선이 필요한 다른 것들을 보게 될 수도 있다. 하지만 이것들을 동시에 고치지는 말라. 한 번에 단 한 가지만 변경하는 과학 실험 방법을 사용하도록 하라. 이를 위한 최선의 과정은, 그 버그를 쉽게 다시 확인할 수 있게 되면, 고친 내용을 바꿔 넣고 나서, 프로그램에서 버그가 더 이상 없다는 것을 확인하는 것이다. 물론 때때로 한 줄 이상을 고쳐야 하겠지만 그렇다 해도 개념적으로는 더 이상 나눌 수 없는(atomic) 한 부분만 변경해서 버그를 고쳐야 한다.

실제로 버그가 여러 개인데 그것들이 하나인 것처럼 보이는 경우도 있다. 버그를 어떻게 정의하여 그것들을 하나씩 고쳐 갈 것인지 결정하는 것은 결국 프로그래머의 몫이다. 프로그램이 무엇을 해야 하는지, 또는 원래 개발자가 의도했던 것이 무엇인지 불분명할 경우도 있다. 그런 경우에는 경험을 근거로 판단을 내리고 그 코드에 자기 나름대로 의미를 부여해야 할 것이다. 그 프로그램이 무엇을 해야 할지 결정하고, 그것에 대해 주석을 달거나 어떤 식으로든 명료화하고, 그 코드가 그 의미에 부합하도록 만든다. 이것은 중급에서 고급의 기능으로서 처음부터 새로운 함수를 작성하는 것보다 더 어려울 경우도 있지만, 현업에서는 이런 귀찮은 일이 종종 생긴다. 어쩌면 자신에게 수정 권한이 없는 시스템을 고쳐야 하게 될지도 모른다.

2.1.4 로그를 이용해서 디버그 하는 방법 

로그 기록(logging)이란 정보를 제공하는 일련의 기록인 로그(log)를 생성하도록 시스템을 작성하는 활동을 말한다. 프린트 줄 넣기(printlining)는 간단한, 보통은 일시적인, 로그를 생성하기만 한다. 완전한 초보자들은 프로그래밍에 대해 아는 것에 한계가 있기 때문에 로그를 이해하고 사용해야 한다. 시스템 설계자들은 시스템의 복잡성 때문에 로그를 이해하고 사용해야 한다. 로그가 제공하는 정보의 양은, 이상적으로는 프로그램이 실행되는 중에도, 설정 가능해야 한다. 일반적으로 로그 기록은 다음의 이점이 있다.
  • 로그는 재현하기 힘든 (예를 들어, 개발 완료된 환경에서는 발생하지만 테스트 환경에서는 재현할 수 없는) 버그에 대한 유용한 정보를 제공할 수 있다.
  • 로그는, 예를 들어, 구문(statement)들 사이에 걸리는 시간과 같이, 성능에 관한 통계와 정보를 제공할 수 있다.
  • 설정이 가능할 때, 로그는 예기치 못한 특정 문제들을 디버그하기 위해, 그 문제들을 처리하도록 코드를 수정하여 다시 적용하지(redeploy) 않아도, 일반적인 정보를 갈무리할 수 있게 한다. 

로그에 남기는 정보의 양은 항상 정보성과 간결성 사이의 타협으로 결정된다. 정보를 너무 많이 남긴다면 로그가 낭비적이 되고 스크롤에 가려지게(scroll blindness) 되어, 필요한 정보를 찾기 어려워질 것이다. 너무 조금 남긴다면 필요한 정보가 남지 않을 것이다. 이런 점에서, 무엇이 출력될지 설정할 수 있게 하는 것은 매우 유용하다. 일반적으로 로그에 남는 기록을 통해, 그 기록을 남긴 소스 코드의 위치, (적용 가능하다면) 문제가 되는 작업을 실행한 쓰레드, 정확한 실행 시각, 그리고 일반적으로, 어떤 변수의 값, 여유 메모리의 양, 데이터 객체의 개수 등 그 밖의 유용한 정보를 알 수 있다. 이러한 로그 생성 구문들은 소스 코드 전체에 흩어져 있는데, 특히 주요 기능이 있는 지점과 위험 부담이 있는 코드 근처에 있다. 구문마다 수준이 정해질 수 있으면 시스템의 현재 설정에 따라 그 수준에 해당하는 기록만 남기게 될 것이다. 로그 생성 구문을 설계할 때에는 어디에서 문제가 생길지 예상해서 그것을 기록으로 남길 수 있게 해야 한다. 성능을 측정할 필요성도 예상하고 있어야 한다.

영구적인 로그를 남긴다면, 로그 기록이 프린트 줄 넣기(printlining)를 대신 할 수 있을 것이고, 디버그 구문들 중에도 로그 기록 시스템에 영구적으로 추가할 것들이 있을 것이다.

2.1.5 성능 문제를 이해하는 방법 

실행중인 시스템의 성능을 알아내는 방법을 이해하는 일은 디버깅과 마찬가지로 피할 수 없는 일이다. 자기가 작성한 코드의 실행에 드는 비용을 완벽하고 정확하게 이해하고 있다 해도, 그 코드는 통제할 수 없거나 들여다볼 수 없는 다른 소프트웨어 시스템들을 호출할 때도 있다. 하지만 실제로 성능의 문제는 일반적으로 디버깅과는 조금 다르고 또 조금은 쉬운 문제이다.

어떤 시스템이나 하위 시스템이 너무 느린 것 같다고 가정해 보자. 그것을 빠르게 하기 전에 우선 왜 그것이 느린지에 대해 머리 속으로 모형을 만들어야 한다. 그렇게 할 수 있도록 시간과 그 밖의 자원들이 어디에 실제로 쓰이고 있는지 알기 위해 성능 기록 도구(profiling tool)나 좋은 로그 기록을 쓸 수 있다. 유명한 격언 중에 90%의 시간은 10%의 코드에 쓰인다는 말이 있다. 나는 그 격언에 성능 문제에서 입출력 시간(I/O)의 중요성을 추가하고 싶다. 종종 대부분의 시간은 이러저러한 방식으로 I/O에 쓰인다. 낭비가 심한 I/O와 그러한 10%의 코드를 찾아냈다면 한 발짝 잘 내딛은 것이다.

컴퓨터 시스템의 성능에는 여러 차원이 있고 여러 자원들이 사용된다. 측정해야 할 첫 번째 자원은 벽시계 시간(wall-clock time), 즉 계산에 걸리는 총 시간이다. 벽시계 시간을 로그에 기록으로 남기는 것은, 다른 성능 기록 방법이 통하지 않는 예상치 못한 상황에 대한 정보를 줄 수 있으므로 특히 가치가 있다. 하지만 이것으로 모든 것을 알 수는 없다. 때로는 시간이 좀 더 걸리기는 하지만 프로세서의 처리 시간을 많이 잡아먹지 않는 코드가 실제로 작업해야 하는 전산 환경에서는 훨씬 더 좋을 수 있다. 마찬가지로, 메모리, 네트웍 대역폭, 데이터베이스, 그 밖의 서버 접속들이 결국에는 프로세서 처리 시간보다 더 낭비가 클 수 있다.

동시에 사용해야 하는 공유 자원 쟁탈(contention)로 교착 상태(deadlock)나 기아 상태(starvation)가 생길 수도 있다. 교착 상태란 동기화나 자원 요청이 부적절하여 더 이상 진행할 수 없는 상태를 말한다. 기아 상태란 구성요소에 시간을 적절히 배분하는 데에 실패한 것이다. 이런 상황을 예상할 수 있다면 프로젝트를 시작할 때부터 이런 쟁탈을 측정할 방법을 마련하는 것이 최선이다. 이러한 쟁탈이 일어나지 않는다 해도 그것을 확실히 검증할 수 있게 해 놓는 것은 매우 도움이 된다.

2.1.6 성능 문제를 해결하는 방법 

대부분의 소프트웨어 프로젝트는 첫 배포판을 냈을 때보다 비교적 적은 노력으로도 10배에서 100배나 더 빠르게 진행될 수 있다. 출시일의 압박 하에서는, 일을 간단하고 신속하게 끝낼 수 있는, 하지만 다른 해결책보다는 효율이 떨어지는, 해결책을 선택하는 것이 어쩌면 현명하고도 효과적인 방법일 수 있다. 하지만 성능은 사용 편이성(usability)의 일부이며, 결국에 가서는 더욱 세심하게 고려해야 할 경우가 많다.

매우 복잡한 시스템의 성능을 향상시키는 열쇠는 병목(bottleneck), 즉 대부분의 자원들이 사용되는 지점을 찾기 위해 충분히 잘 분석하는 것이다. 계산 시간의 1% 밖에 차지하지 않는 함수를 최적화하는 것은 별 의미가 없다. 실제로 시간이 어디에 쓰이는지 알아내기 위해 성능 분석을 먼저 해야 하며, 그로부터 무엇을 향상시킬 것인지 결정할 수 있다. 경험상으로 볼 때, 어떤 작업이 시스템이나 시스템의 중요한 부분을 최소한 두 배 빠르게 할 것이라고 생각되지 않는다면, 그것을 실행에 옮기기 전에 신중하게 생각해야 한다. 보통 이것을 위해 쓰는 방법이 있다. 그 변화에 따라 필요하게 되는 검사와 품질 확인의 수고를 고려하라. 모든 변화에는 검사라는 짐이 따르므로 큰 변화가 적을수록 더 좋은 것이다.

어디에선가 두 배의 향상을 달성한 후에는, 최소한 다시 생각하고 또 다시 분석하여 그 다음으로 낭비가 심한 병목이 어디인지 발견해 내고, 또 다른 두 배의 성능 향상을 이루기 위해 그 지점을 공략해야 할 것이다.

성능 상 병목은 소를 셀 때 머리를 세는 대신 다리를 센 다음 4로 나누는 것에 비유할 수 있을 것이다. 예를 들어, 나는 어떤 관계형 데이터베이스 시스템에서 자주 검색하는 열에 적절한 인덱스를 달지 않아서, 검색이 최소한 스무 배는 느려지는 오류를 일으킨 적이 있다. 그 외에도, 내부 반복문에서 불필요한 I/O를 하는 것, 더 이상 필요 없는 디버그 구문을 남겨 놓는 것, 불필요한 메모리 할당, 성능에 대해 제대로 문서화되어 있지 않은 라이브러리나 그 밖의 하위 시스템들을 전문적인 안목 없이 사용하는 것 등을 예로 들 수 있을 것이다. 이런 식의 성능 향상을, 쉽게 따서 성과를 낼 수 있다는 의미에서, 낮게 달린 과일(low-hanging fruit)이라고 부르기도 한다.

낮게 달린 과일들을 거의 다 따 버렸다면 어떻게 할 것인가? 아마도 더 높이 손을 뻗거나 나무를 베어 내릴 것이다. 즉, 조그만 성능 향상을 계속해 갈 수도 있고, 시스템이나 하위 시스템을 진지하게 재설계할 수도 있을 것이다. (이것은, 새로운 설계라는 측면뿐만 아니라 자기 상사에게 이것이 좋은 생각이라는 것을 설득한다는 측면에서도 좋은 프로그래머로서 자신의 능력을 발휘할 수 있는 훌륭한 기회이다.) 하지만, 재설계를 주장하기 전에는 이것이 하위 시스템을 다섯 배에서 열 배는 더 낫게 할 수 있는지 스스로 질문해 봐야 한다.

2.1.7 반복문을 최적화하는 방법 

때때로 제품에서 실행하는 데 시간이 오래 걸리거나 병목(bottleneck)이 되는 반복문이나 재귀함수를 보게 될 것이다. 그 반복문을 조금 빠르게 고치려고 하기 전에, 혹시 그것을 완전히 제거할 방법은 없는지 잠시 생각해 보라. 다른 알고리듬으로 그 일을 할 수 없을까? 다른 계산을 하면서 동시에 그 계산을 할 수는 없을까? 그런 식의 방법을 찾을 수 없다면 반복문 최적화 작업을 해도 된다. 이 일은 단순하다. 잡동사니를 없애 버려라. 결국 이 일은 독창성뿐만 아니라 그런 종류의 구문이나 식에 드는 비용에 대한 이해가 요구된다. 여기 몇 가지를 제안해 보겠다.
  • 실수 연산(floating point operation)을 제거하라
  • 필요 없이 메모리 블록을 새로 할당하지 말라
  • 상수들을 미리 계산하라(fold together)
  • I/O는 버퍼로 옮겨라
  • 나눗셈을 피하라
  • 쓸데없는 형변환(cast)을 피하라
  • 첨자(index)를 반복 계산하는 것보다는 포인터를 옮기는 것이 낫다 

이러한 연산에 드는 비용은 사용하는 시스템에 따라 다르다. 어떤 시스템의 컴파일러와 하드웨어는 알아서 이런 일들을 해 준다. 그래도 분명하고 효율적인 코드가 특정 시스템에 대한 이해가 필요한 코드보다 더 낫다.

2.1.8 I/O 비용을 다루는 방법 

많은 문제에서 프로세서는 하드웨어 장치들과 통신하는 데 드는 시간에 비해 빠르게 동작한다. 이런 시간 비용을 보통 줄여서 I/O라고 하고, 여기에는 네트웍 시간, 디스크 I/O, 데이터베이스 질의, 파일 I/O, 그리고 프로세서에 가깝지 않은 어떤 하드웨어가 뭔가를 하게 하는 작업들이 포함된다. 따라서 빠른 시스템을 만든다는 것은, 어떤 빽빽한 반복문 속에 있는 코드를 개선하거나 더 나아가 알고리듬을 개선하는 것보다 I/O를 개선하는 일이 될 경우가 많다.

I/O를 개선하는 두 가지 매우 기초적인 방법, 즉 캐쉬와 효율적 데이터 표현(representation)이 있다. 캐쉬는 (일반적으로 어떤 추상적인 값을 읽어 오는) I/O를 피하기 위해 그 값을 가깝게 복사해 놓아서 그 값을 가져오기 위해 I/O를 다시 발생시키지 않는 것을 말한다. 캐쉬의 핵심은 어떤 데이터가 원본(master)이고 어떤 데이터가 복사본인지 분명하게 구분하는 것이다. 원본은 단 하나만 있다! 캐쉬는 복사본이 원본의 변화를 즉시 반영하지 못하는 경우가 있다는 위험 부담이 있다.

효율적 데이터 표현이란 데이터를 더욱 효율적으로 표현하여 I/O의 비용을 줄이는 방식이다. 이 방법은 종종 가독성과 호환성 등의 다른 요구 조건과 대립되기도 한다.

효율적 데이터 표현은 처음의 표현 방식보다 두세 배의 개선 효과가 있기도 하다. 이를 위한 방법들로는 사람이 읽을 수 있는 표현 방식 대신 2진 표현 방식을 사용하는 것, 데이터와 함께 심벌 사전을 전송하여 긴 심벌들을 인코딩할 필요가 없게 하는 것, 그리고 극단적으로는 허프만(Huffman) 인코딩 같은 것 등이 포함된다.

때때로 가능한 세 번째 방법은 계산 부분을 데이터에 밀착시켜 더 가까운 곳에서 참조할 수 있게 하는 것이다. 예를 들어, 데이터베이스에서 어떤 데이터를 읽어 와서 합계와 같은 간단한 계산을 한다고 할 때 데이터베이스 서버가 직접 그 작업을 하게 하는 것이다. 이 방법은 작업하는 시스템의 특성에 매우 많이 의존하기는 하지만, 시험해 볼 필요는 있다.

2.1.9 메모리를 관리하는 방법 

메모리는 절대로 다 써 버리면 안 되는 소중한 자원이다. 잠시 동안은 그것을 무시할 수 있겠지만 결국에는 메모리를 어떻게 관리할 것인지 결정해야 할 것이다.

단일 서브루틴이 차지하는 범위 이상으로 유지될 필요가 있는 공간을 종종 할당된 힙(heap)이라고 한다. 아무도 참조하지 않는 메모리 영역은 쓸모없는 쓰레기(가비지, garbage)일 뿐이다. 사용하는 시스템에 따라 메모리가 가비지가 될 것 같으면 명시적으로 메모리 할당을 해제해야 한다. 가비지 수집기를 제공하는 시스템을 사용할 수 있을 경우도 많다. 가비지 수집기는 가비지를 발견하면 프로그래머의 어떤 조작도 필요 없이 그것이 차지하는 공간을 풀어준다. 가비지 수집은 훌륭한 방법이다. 이를 통해 오류를 줄이고, 적은 노력으로도 코드를 더욱 간결하게 할 수 있다. 할 수 있다면 이 방법을 사용하라.

하지만 가비지 수집을 한다 해도 모든 메모리를 가비지로 채우게 될 수 있다. 고전적인 실수 중의 하나는 해쉬 테이블(hash table)을 캐쉬로 사용하고는 해쉬 테이블에 있는 참조 주소들을 제거하는 것을 잊어버리는 것이다. 참조 주소가 남아 있으므로 그 주소에 해당하는 메모리 영역은 가비지 수집이 될 수 없는 상태로 못 쓰게 된다. 이것을 메모리 누수(leak)라고 한다. 메모리 누수는 일찍부터 찾아서 고쳐야 한다. 장시간 실행되는 시스템이 있다면 검사할 때는 메모리가 고갈되는 일이 없다가 실제로 사용될 때가 돼서야 고갈되기도 한다.

새로운 객체의 생성은 어떠한 시스템에서도 어느 정도 시간 비용이 드는 작업이다. 하지만 서브루틴의 지역 변수들(local variables)에 직접 할당된 메모리는 할당 해제 방식이 매우 간단해질 수 있으므로 그렇게 시간 비용이 들지는 않는다. 어떻든 불필요한 객체 생성은 피해야 한다.

한 번에 필요한 객체 수의 상한을 정할 때 생기는 중요한 경우가 있다. 이 객체들이 모두 같은 양의 메모리를 필요로 한다면 그것들을 모두 수용하기 위해 단일 블록의 메모리, 즉 버퍼를 할당할 수 있을 것이다. 그 객체들은 이 버퍼 안에서 정해진 순환 방식에 따라 할당되고 해제될 수 있으므로 이 버퍼를 링 버퍼(ring buffer)라고 부르기도 한다. 이것은 보통 힙 할당보다 더 빠르다.

때로는 할당된 공간이 다시 할당될 수 있도록, 가비지 수집에 의존하는 대신, 그것을 명시적으로 풀어줘야 한다. 그래서 할당된 각 영역을 잘 알아내어 그것을 적절한 때에 해제하는 방법을 설계해야 한다. 그 방법은 생성된 객체의 종류에 따라 달라질 수 있다. 메모리 할당 작업이 수행될 때마다 그것이 결국에는 메모리 할당 해제 작업과 짝을 이뤄야 한다는 사실을 명심해야 한다. 이것은 매우 어렵기 때문에 프로그래머들은 이를 위해 단순하게 참조 회수 세기와 같은 기초적인 형태의 가비지 수집 방법을 구현하는 경우도 있다.

2.1.10 가끔씩 생기는 버그를 다루는 방법 

가끔씩 생기는 버그는 "외계에서 온 20미터짜리 투명 전갈"의 사촌 같은 종류의 버그이다. 이 끔찍한 악몽은 관찰하기 어려울 만큼 가끔씩 나타나지만, 또한 무시할 수 없을 만큼 자주 일어난다. 이런 버그는 발견하기도 어렵게 때문에 고치기도 어렵다.

그 버그를 찾기 위해 여덟 시간을 매달린 뒤에 그것이 정말 있는 것인지 의심하기 시작한다 해도, 가끔씩 생기는 버그는 다른 모든 것들이 따르는 동일한 논리 법칙을 따를 수밖에 없다. 이 버그를 상대하기 힘든 것은 알지 못하는 어떤 조건들에서만 생기기 때문이다. 그 버그가 생기는 바로 그 때의 상황들을 기록하여 정말로 어떤 변이가 생긴 것인지 추측할 수 있도록 해 보라. 그 조건은, 예를 들어, '이 버그는 '와이오밍(Wyoming)'이라는 값을 입력했을 때만 생긴다"는 것과 같이, 데이터 값에 관련되어 있을 수도 있다. 만약 이것이 변이의 원인이 아니라면 다음으로는 동시에 수행되어야 하는 작업이 실제로는 그렇게 되지 못했을 경우를 의심해 볼 수 있다.

어떤 통제된 방식으로 그 버그가 다시 나타나도록 계속, 계속, 계속 시험해 보라. 다시 나타나게 할 수 없다면, 버그가 실제로 발생할 때 그것을 분석하기 위해 필요하다고 생각되는 정보들을 기록으로 남길 수 있는 (필요하다면 특별한) 로그 기록 시스템을 만들어 그 버그 앞에 덫을 놓아 보라. 버그가 개발 환경에서는 나타나지 않고 완성 제품에서만 나타난다면 그것은 오래 걸리는 프로세스 때문일 것이라고 믿어 보라. 로그 기록에서 얻는 실마리들은 해결책을 제공해 주지는 못하더라도 로그 기록 방법을 개선하기 위한 정보는 충분히 줄 수 있을 것이다. 개선된 로그 기록 시스템을 완성하는 데에는 오랜 시간이 걸릴 수도 있다. 그리고는 더 많은 정보를 얻기 위해 그 버그가 다시 나타날 때까지 기다려야 한다. 이 일은 어느 시간 동안 계속 반복해야 할 수도 있다.

가끔씩 생기는 버그들 중에서 내가 저지른 가장 어리석었던 것은, 어떤 수업의 프로젝트에서 함수형 프로그래밍 언어(functional programming language)를 다중 쓰레드로 구현하는 것이었다. 나는 그 함수형 프로그램이 모든 (이 수업에서는 여덟 개의) CPU들을 잘 활용하여 수치 계산을 동시에 정확하게 해 내도록 매우 주의를 기울였다. 그런데 가비지 수집기를 동기화하는 것을 깜빡 잊었다. 이 시스템은 오랜 시간 동안 잘 돌아갔고, 뭔가 이상하다는 것을 눈치 채기 전까지는, 어떤 작업을 시작하든 잘 마무리되는 것 같았다. 부끄럽게도 나는 내 실수가 드러나기 전까지는 하드웨어에 문제가 있다고 생각했었다.

최근에 일하면서는, 발견하기까지 몇 주나 걸렸던 가끔씩 생기는 버그를 만난 적이 있다. 우리에게는 아파치(Apache) 웹 서버 뒤에 자바(Java)로 구현된 다중 쓰레드 어플리케이션 서버들이 있다. 페이지 전환을 빠르게 유지하기 위해 우리는 모든 I/O가 페이지 전환 쓰레드들과는 다른 네 개의 독립된 쓰레드에서 일어나게 하고 있다. 그런데 이것들이 (우리 로그 기록에 따르면) 가끔 한 번씩 몇 시간 동안 멈춘 것처럼 되면서 아무 일도 하지 않았다. 쓰레드가 네 개가 있기 때문에 네 개 모두 멈추지 않는 한 그 자체로는 큰 문제는 아니었다. 그렇게 된다면 이 쓰레드들이 비워내는 큐(queue)가 남아 있는 모든 메모리를 순식간에 다 채워서 서버가 멈춰버렸을 것이다. 이것을 알게 되기까지 한 주 정도 걸렸지만 무엇 때문에 이런 일이 생기는지, 언제 생길지, 또는 멈출 때 어느 쓰레드가 어디쯤 작업을 하고 있는지조차 여전히 몰랐다.

이것은 타사 소프트웨어와 연관된 위험성을 보여준다. 우리는 텍스트에서 HTML 태그를 제거하는 코드의 사용권을 받아서 쓰고 있었다. 우리는 그 코드가 나온 나라 이름을 따서 그것을 '프랑스 스트리퍼'라는 애칭으로 불렀다. 우리는 소스 코드를 가지고 있었지만 (감사하나이다!) 우리 서버의 로그 기록을 살펴보다가 이메일 쓰레드들이 프랑스 스트리퍼에서 멈춘다는 것을 알게 되기까지 그 소스를 주의깊게 연구하지 않았다.

이 스트리퍼는 제대로 동작했지만 길이가 길고 특이한 텍스트에 대해서는 그렇지 못했다. 이런 텍스트를 처리하는 데에는 텍스트 길이의 제곱에 비례하거나 더 많은 시간이 걸렸다. 이런 텍스트들이 자주 나타나는 것이었다면 우리는 그 버그를 금방 발견했을 것이다. 그것이 전혀 나타나지 않았다면 우리는 전혀 문제가 없었을 것이다. 하지만 그것은 나타났고, 우리는 문제를 이해하고 해결하는 데 몇 주나 걸렸던 것이다.

2.1.11 설계 기능을 익히는 방법 

소프트웨어를 설계하는 방법을 배우기 위해서는 사수(mentor)가 설계를 할 때 그들과 한 자리에 있으면서 그들의 행동을 연구하라. 그리고 잘 작성된 소프트웨어를 연구하라. 그 후에는 최근에 나온 설계 기법에 대한 책을 읽으라.

그리고 그것들을 직접 실천해야 한다. 소규모 프로젝트부터 시작하라. 최종 작업을 마쳤으면 그 설계가 어떻게 실패하거나 성공했는지, 처음 생각했던 개념에서 어떻게 달라졌는지 숙고하라. 이런 식으로 다른 사람들과 더 큰 프로젝트를 수행하게 된다. 설계 기능은 판단 능력의 문제이며, 그것을 얻기까지 몇 년은 걸린다. 현명한 프로그래머는 두 달이면 적당한 수준에서 기본 기능을 익힐 수 있을 것이며 그것을 기반으로 발전해 갈 것이다.

자기 자신의 스타일을 개발하는 것은 자연스러운 일이며 자신에게 도움이 될 것이다. 설계는 예술이지 과학이 아니라는 것을 명심하라. 이런 주제로 책을 쓰는 사람들은 흔히 그것이 과학적으로 보이게 하는 데에 관심을 갖는다. 하지만 특정한 설계 스타일을 고집하지는 않도록 하라.

2.1.12 실험을 수행하는 방법 

이제는 고인이 된 위대한 에처 다익스트라(Edsger Dijkstra)는 전산 과학은 실험 과학이 아니며<ExpCS> 전자적인 컴퓨터에 의존하지 않는다고 설득력 있게 설명했다. 그가 1960년대의 전산 과학에 대해 다음과 같이 말했다. <Knife>

해로운 일이 일어났다. 이 주제는 '전산 과학'으로 알려지기 시작했다. - 이것은 꼭 외과 수술을 '칼 과학'이라고 부르는 것과 같다. - 그리고 전산 과학은 기계와 그 주변 장치들에 대한 학문이라고 사람들의 생각 속에 단단히 자리를 잡게 되었다. 


프로그래밍이 실험 과학이 되어서는 안 되겠지만, 대부분의 실무 프로그래머들은 다익스트라가 전산 과학에 대해 내린 정의에 동감할 만큼 여유롭지는 못하다. 우리는, 전부는 아니더라도 일부 물리학자들이 그러하듯이, 실험의 영역 속에서 일해야 한다. 지금부터 30년 동안 프로그래밍이 실험 없이 이뤄질 수 있다면, 그것은 전산 과학의 위대한 승리라고 할 수 있을 것이다.

앞으로 해 보게 될 몇 가지 실험들은 다음과 같다.
  • 문서 내용에 부합하는지 검증하거나 (문서가 없다면) 시스템의 반응을 이해하기 위해 몇몇 표본들로 시스템을 검사하기
  • 실제로 버그가 고쳐졌는지 알아보기 위해 코드의 바뀐 부분을 검사하기
  • 시스템의 성능 특성을 완벽하게 알기 위해 두 가지 다른 조건에서 시스템의 성능을 측정하기
  • 데이터의 무결성을 점검하기
  • 어려운, 혹은 반복하기 힘든 버그 해결의 실마리를 얻기 위해 통계 자료를 수집하기 

내가 이 글에서 실험 설계를 설명할 수 있으리라고는 생각하지 않는다. 독자 스스로 연구하고 연습해 보아야 할 것이지만, 두 가지 짧은 조언을 해 줄 수는 있겠다.

첫째, 자신의 가설이나 점검하려고 하는 검증 조건(assertion)이 무엇인지 명쾌해야 한다. 혼돈스럽거나 다른 사람들과 함께 작업할 때는 가설을 적어 보는 것이 도움이 되기도 한다.

각 실험이 지난 실험에서 얻은 지식에 기반을 두는 일련의 실험들을 설계해야 하는 상황에 처할 때가 종종 있을 것이다. 그러므로 가능한 한 많은 정보를 얻어낼 수 있도록 실험을 설계해야 한다. 불행히도 이것은 각 실험이 단순명료해야 한다는 조건과 대립된다. 이런 상황에 대한 판단은 경험을 통해 개발해야 할 것이다.

2.2 팀의 기능들 

2.2.1 시간 추정이 중요한 이유 

소프트웨어 시스템이 최대한 빨리 적극적으로 활용되게 하기 위해서는 개발 계획을 세우는 것뿐만 아니라 문서화, 시스템 배치, 마케팅 등의 계획도 세워야 한다. 상업적인 프로젝트에서는 영업과 재무도 포함한다. 개발 시간을 예측할 수 없다면 이러한 것들을 효과적으로 계획하는 것은 불가능하다.

정확한 시간 추정은 예측 가능성을 높인다. 관리책임자들은 그렇게 되는 것을 매우 좋아하며, 또 당연히 그래야 한다. 관리책임자들은 소프트웨어를 개발하는 데 얼마나 시간이 걸릴지 정확하게 예측하는 것이 이론적으로나 실제적으로 불가능하다는 사실을 전혀 이해하지 못하는 경우가 있다. 우리는 이런 불가능한 일을 하도록 항상 요구받으므로 그 사실을 정직하게 대면해야 한다. 어쨌든 이 과제가 불가능하다는 것을 인정하지 않는 것은 부정직한 일이 될 것이므로 필요하다면 그것을 설명하라. 사람들은,

그 문제를 제대로 이해한 것이라면, (아무도 그 동안 우리를 방해하지 않는다고 할 때) 5주 안에 마칠 가능성이 50% 정도 있다고 추정됩니다. 


위의 문장이 실제로는,

그 일을 모두 5주 안에 틀림없이 마치겠습니다. 


위의 문장을 뜻한다고 부풀려 생각하는 놀라운 경향이 있으므로, 시간 추정에 대해 의사소통의 문제가 생길 가능성이 많다.

이러한 일상적인 해석의 문제가 있으므로 상사나 고객이 아무 것도 모른다 생각하고 그 시간 추정이 무엇을 의미하는 것인지 분명하게 논의해야 한다. 그리고 예상 시간이 아무리 틀림없어 보여도 다시 한 번 더 그것을 추정해 보아야 한다.

2.2.2 프로그래밍 시간을 추정하는 방법 

시간 추정은 연습이 필요하다. 또한 노력해야 한다. 시간 추정은 노력이 많이 드는 일이기 때문에, 특히 대규모 작업 시간을 추정해야 할 경우에는 시간 추정 자체에 드는 시간을 추정할 필요도 있다.

대규모 작업 시간을 추정해야 할 때에는 천천히 하는 것이 가장 정직한 것이다. 대부분의 기술자들은 열심이 있으며 사람들을 기쁘게 하고 싶어 하기 때문에 천천히 하는 것을 분명히 좋아하지 않을 것이다. 하지만 즉석으로 추정한 것은 대개 정확하지도 정직하지도 않다.

천천히 하는 동안 그 과제를 수행하거나 과제의 모형을 만드는 것에 대해 숙고할 수 있을 것이다. 정책의 압력을 피할 수 있다면 이것이 시간 추정을 도출할 수 있는 가장 정확한 방법이며, 실제로도 그대로 작업이 진행될 수 있을 것이다.

조사할 시간을 충분히 갖는 것이 불가능할 때에는, 우선 그 추정이 무엇을 뜻하는지 아주 분명하게 확정해야 한다. 추정 내용 기록의 첫 부분과 마지막 부분에 그 의미를 다시 써 놓으라. 과제를 점진적인 소규모 하위 과제들로 분해하되 각 소규모 과제가 하루 이상이 되지 않을 때까지, 이상적으로는 그 이하의 길이로 추정 내용 기록을 준비하라. 가장 중요한 것은 하나라도 빠뜨리는 일이 없도록 하는 것이다. 예를 들어, 문서화, 검사, 계획 시간, 다른 그룹과 회의 시간, 휴가 시간 등이 모두 매우 중요하다. 매일 바보들을 상대하며 보내는 시간이 있다면 그것도 추정 내용에 한 항목으로 적어 넣으라. 이렇게 하면 최소한 무엇 때문에 시간을 써 버리게 되는지 상사가 알아볼 수 있게는 될 것이며, 자신의 시간을 지킬 수 있게 될 것이다.

추정 시간을 은연중에 불려서 쓰는 기술자들도 있지만, 나는 그렇게 하지 말라고 권하고 싶다. 불려서 쓰는 것이 낳는 결과들 중 하나는 다른 사람의 신뢰를 잃게 된다는 것이다. 예를 들어, 어떤 기술자가 사실은 하루가 걸릴 것이라고 생각되는 어떤 과제를 3일로 추정했다고 하자. 그 기술자가 나머지 이틀은 문서 작업을 하거나 어떤 다른 유용한 프로젝트를 하겠다고 계획했을 수 있다. 하지만 그 과제가 단 하루 만에 끝났다는 것은 (그 사실이 알려졌다면) 추적할 수 있을 것이며, 일을 늦추거나 과대 추정했다는 기색이 보일 것이다. 자신이 실제로 하고 있는 것을 적절히 볼 수 있게 하는 것이 더욱 좋다. 문서 작업하는 데 드는 시간이 코딩 시간의 두 배가 된다는 사실을 시간 추정 내용에서 확인할 수 있다면, 이것을 관리책임자가 볼 수 있게 하는 것이 더욱 큰 이득이 될 것이다.

추정 시간은 숨김없이 불려서 쓰라. 어떤 과제가 하루가 걸릴 것 같은데, 현재 접근 방법이 들어맞지 않을 경우 열흘이 걸릴 수도 있다면, 이 사실을 어떻게든 추정 내용에 적으라. 그렇지 않으면 적어도 추정한 확률을 가중치로 한 평균값을 계산하라. 알 수 있고 추정할 수 있는 위험 부담 요소는 모두 일정에 들어가야 한다. 한 사람이라면 주어진 기간 동안 아프지 않을 수 있다. 하지만 여러 기술자들이 함께 하는 큰 프로젝트에서는 환자가 생기는 시간도 있고 휴가 기간도 있다. 필수로 참석해야 하는 회사의 연수 세미나가 일어날 확률은 얼마나 될까? 추정할 수 있다면 꼭 끼워 넣으라. 물론 알려지지 않은 미지수의 일, 즉 예측불허의 일들도 있다. 예측불허란 개별적으로는 추정할 수 없다는 말이다. 모든 예측불허의 일에 대해 공통적으로 쓰이는 한 항목을 만들거나, 상사와 의사소통하는 그 밖의 방식으로 그것들을 처리할 수 있을 것이다. 하지만 상사가 그것들의 존재를 잊어버리게 하지는 말라. 예측불허의 일을 생각하지 않고 시간을 추정한 그대로 일정이 잡히기는 지독하게도 쉽다.

팀 환경에서는, 그 일을 하는 사람이 그 시간을 추정하도록 해야 하며, 전체 추정 시간에 대해 팀 전체가 합의하도록 해야 한다. 사람들은 능력, 경험, 준비도, 자신감에 큰 차이가 있다. 능력 있는 프로그래머가 자기에게 맞게 추정한 것을 능력이 부족한 프로그래머들도 따라 하다가는 큰 문제가 생긴다. 팀 전체가 추정 내용에 대해 한 줄, 한 줄씩 동의하게 함으로써 팀 전체의 이해를 분명히 하고, 자원을 전술적으로 재분배할 수 있는 (예를 들어, 능력이 부족한 팀원에게서 능력 있는 팀원에게 짐을 넘기는) 기회도 생긴다.

수치로 나타낼 수 없는 큰 위험 부담이 있다면, 프로그래머는 관리책임자가 함부로 거기에 뛰어들었다가 그 위험이 발생했을 때 당황하지 않도록 강력하게 말해 줄 의무가 있다. 바라기에는 그 경우에 그 위험 부담을 줄이기 위해 필요한 모든 일을 하게 될 것이다.

익스트림 프로그래밍(Extreme Programming) 기법을 사용하도록 회사를 설득할 수 있다면 비교적 적은 것들만 추정해도 될 것이며, 더 재미있으면서 더 생산적으로 일할 수 있을 것이다.

2.2.3 정보를 찾는 방법 

알아야 하는 정보의 성격에 따라 그것을 찾는 방법이 결정된다.

어떤 소프트웨어의 최근 패치 단계와 같이 객관적이고 검증하기 쉬운 구체적인 것들에 대한 정보가 필요하다면, 그 정보에 대해 인터넷을 검색하거나 토론 그룹에 글을 올려 많은 사람들에게 공손히 물어 보라. 의견이나 주관적인 해석의 낌새가 있는 것은 절대 인터넷에서 검색하지 말라. 허튼 소리가 진실로 취급되는 비율이 매우 놓다.

어떤 것에 대한 생각이 변화해 온 역사와 같이 주관적인 것에 대한 일반적인 지식을 원한다면, (실제 건물이 있는) 도서관으로 가라. 예를 들어, 수학이나 버섯이나 신비주의에 대해 알고 싶다면 도서관으로 가면 된다.

사소하지 않은 어떤 일을 하는 방법을 알고 싶다면 그 주제에 대한 책을 두세 권 사서 읽으라. 소프트웨어 패키지를 설치하는 방법 같은 사소한 일을 하는 방법은 인터넷에서 배울 수 있다. 좋은 프로그래밍 기법 같은 중요한 것들을 인터넷에서 배울 수도 있지만, 그 결과를 검색하고 분류하거나 그 결과가 믿을 만한 것인지 파악하느라, 손으로 잡을 수 있는 책의 적당한 부분을 찾아 읽는 데 드는 시간보다 더 많은 시간을 낭비할 가능성이 크다.

"이 신제품 소프트웨어는 대규모의 데이터를 처리할 수 있는가?"와 같이 아무도 알 것 같지 않은 정보가 필요하다면, 어쨌든 인터넷이나 도서관을 검색해 봐야 할 것이다. 열심히 찾아도 찾을 수 없다면 그것을 확인하기 위한 실험을 설계해야 할 것이다.

어떤 특정한 상황에 대한 의견이나 가치 판단을 원한다면, 전문가에게 이야기하라. 예를 들어, 최신 데이터베이스 관리 시스템(DBMS)을 LISP로 만드는 것이 좋은 생각인지 알고 싶다면 LISP 전문가와 데이터베이스 전문가와 이야기해야 한다.

어떤 응용프로그램을 위한, 아직 발표되지 않은 더 빠른 알고리듬이 있는지 알고 싶다면 그 분야에서 일하는 사람과 이야기해야 할 것이다.

사업을 시작할 것인지 말 것인지에 대한 결정과 같이 자신만이 내릴 수 있는 개인적인 결정을 하려고 한다면, 그 생각에 대한 긍정적인 조건과 부정적인 조건을 나열해서 적어 보라. 그래도 결정을 못하겠다면 점(divination)을 쳐 보라. 어떤 생각에 대해 다각도로 연구해 봤고, 해야 할 일을 다 했고, 모든 결과와 긍정적, 부정적 조건들을 다 따져 봤다 해도, 아직은 결정하지 마라. 이제 머리는 잠시 쉬고 마음의 움직임을 따라 보라. 잘 의식하지 못하는 자신의 욕망을 파악하는 데에는 다양한 점 기법들이 유용하게 쓰일 수 있다. 이 기법들이 보여 주는 모호하고 무작위적인 패턴에 대해 자신의 잠재의식이 의미를 부여하게 될 것이기 때문이다.

2.2.4 사람들을 정보의 원천으로 활용하는 방법 

각 사람의 시간을 존중하고 자신의 시간과 균형을 맞추라. 어떤 사람에게 질문하는 것은 답을 얻는다는 것 이상의 것을 이루게 한다. 그 사람은 나의 존재를 인정하고 특정 질문에 귀를 기울이면서 나에 대해 알게 된다. 나도 마찬가지로 그 사람을 알게 되며, 찾던 답도 알 수 있게 된다. 대개의 경우 이것이 질문 자체보다 더욱 중요하다.

하지만 그것을 반복하게 되면 그 가치는 줄어든다. 질문을 한다는 것은 결국 어떤 사람이 갖고 있는 소중한 필수품, 즉 그들의 시간을 사용하는 것이다. 의사소통의 소득은 그 비용과 비교하여 저울질해 보아야 한다. 게다가 그 비용과 소득은 사람마다 다르다. 나는 100명의 부하 직원이 있는 사람은 그 조직의 모든 사람들에게 한 달에 5분씩은 이야기할 시간이 있어야 한다고 확신한다. 이것은 자기 시간의 약 5%를 할애하는 것이 될 것이다. 하지만 10분은 너무 많은 것 같고, 같은 5분이라 해도 직원이 1,000명이라면 그것도 너무 많다. 자신의 조직에 속한 사람들에게 이야기하는 시간의 양은 그 사람들의 (직위보다는) 역할에 따라 다르다. 상사에게 이야기하는 시간이 상사의 상사에게 이야기하는 시간보다는 더 많아야 한다. 불편할 수도 있겠지만, 무엇에 대해서든 자신의 모든 상급자들에게 매달 조금씩 이야기를 할 의무가 있다고 생각한다.

여기에서 기본 법칙은, 우리에게 잠깐 이야기를 하는 사람은 모두 이득을 얻지만, 이야기가 길어질수록 그 이득은 적어진다는 것이다. 우리는 그들에게 이 이득을 나눠주고 그들과 대화하면서 이득을 받아와야 하며, 들인 시간과 이득 사이에 균형을 맞춰야 한다.

자신의 시간을 존중하는 것도 중요하다. 누군가에게 이야기하는 것을 통해, 그들의 시간을 쓰게 한다 해도, 자신의 시간을 많이 아낄 수 있다면, 그 사람의 시간이 나의 시간보다 더 소중하다고 생각하지 않는 한, 그것을 해야 한다. 우리 부족(tribe)에 대해서도 마찬가지이다.

특이한 사례로서 여름방학 인턴의 경우를 들 수 있다. 고급 기술직에 들어온 여름방학 인턴이 무슨 큰 일을 해 낼 것이라고 기대하기는 힘들다. 그들은 단지 거기 있는 모든 사람들을 괴롭게 할 것이라고 기대할 수 있을 것이다. 그렇다면 왜 이걸 참아야 하는가? 괴롭힘을 받는 사람은 그 인턴에게서 중요한 것을 얻게 될 것이기 때문이다. 그들은 살짝 자랑할 수 있는 기회가 생긴다. 새로운 아이디어를 들을 기회가 생길지도 모른다. 다른 시각에서 관찰해 볼 기회가 생긴다. 그 인턴을 채용하려는 의도가 있을 수도 있지만, 그렇지 않다 하더라도 얻는 것이 많다.

솔직히 누군가가 도움이 되는 말을 해 줄 수 있겠다는 생각이 들면, 그들에게 지혜와 판단력을 나눠 달라고 부탁해야 한다. 그러면 그 사람들은 뿌듯해 할 것이며, 나는 그들에게 무엇인가를 배우고 또 무엇인가를 가르쳐 주게 될 것이다. 좋은 프로그래머에게 영업 부사장의 조언은 별로 필요가 없겠지만, 만약 필요하게 된다면 반드시 조언을 구해야 한다. 나는 우리 영업부 직원들이 하는 일을 더 잘 이해하기 위해서 영업 관련 전화 통화 내용 몇 건을 같이 듣게 해 달라고 요청한 적도 있다. 30분도 채 안 걸린 일이었지만 그런 작은 노력이 영업부에 좋은 인상을 주었다고 생각한다.

2.2.5 현명하게 문서화하는 방법 

아무도 읽지 않을 쓰레기 같은 글을 쓰기에는 인생은 너무 짧다. 쓰레기 같은 글은 아무도 읽지 않을 것이다. 따라서 문서화를 조금이라도 잘 하는 것이 최선이다. 관리책임자들은 이것을 이해하지 못하는 경우가 많다. 질이 떨어지는 문서를 보면서도 그들이 프로그래머들에게 의존하지 않고 있다는 헛된 안도감을 갖게 하기 때문이다. 누가 나에게 내가 작성한 문서가 정말 쓸모없다고 단호하게 말한다면, 그 말을 인정하고 조용히 다른 직업을 찾아보는 게 좋을 것이다.

문서를 잘 만들어내는 데 걸리는 시간의 양을 정확히 추정하여 문서화에 대한 요구를 완화시키는 데 드는 시간을 추정하는 데 이용하는 것만큼 효과적인 일은 없을 것이다. 진실은 냉혹하다. 문서화는 제품 검사처럼 코드 개발보다 더 긴 시간이 걸릴 수 있다.

문서화를 잘 하기 위해서는 우선 작문 실력이 있어야 한다. 작문에 대한 책을 찾아서 공부하고 실습해 보기를 권한다. 글이 깔끔하지 못하고 문서에 써야 하는 언어를 잘 모른다 해도, 다음의 황금률을 따르면 된다. "무엇이든지 남에게 대접을 받고자 하는 대로 너희도 남을 대접하라." 시간 여유를 가지고서 이 문서를 읽을 사람이 누구이며, 그 사람이 무엇을 얻고 싶어 할지, 그리고 그것을 어떻게 가르쳐 줄 수 있을지 진지하게 생각해 보라. 그렇게만 해도 평균 수준 이상의 문서는 만들어낼 수 있을 것이며 프로그래머로서도 훌륭하게 될 것이다.

코드 자체에 대해 문서화를 하게 될 때에는, 프로그래머가 아닌 사람들이 주로 읽을 문서를 작성할 때와는 반대로, 내가 아는 최고의 프로그래머들이 모두 공감하고 있는 바와 같이, 보면 바로 알 수 있게 코드를 작성하고, 코드만으로 그 의미가 분명하지 않은 곳에만 코드에 대해 문서화하라. 이렇게 하는 것이 좋은 두 가지 이유가 있다. 첫째로, 코드 수준의 문서를 봐야 하는 사람이라면 대부분의 경우에 어떻게든 그 코드를 읽을 수 있으며 그것을 선호하기 때문이다. 물론, 이것은 초보보다는 경력 있는 프로그래머에게 더 적당한 말이다. 하지만 더 중요한 것은, 따로 문서화하지 않을 경우, 코드와 문서가 불일치할 리가 없을 것이라는 사실이다. 소스 코드는 아무리 잘못되어 봐야 틀리거나 혼돈스러울 뿐이다. 하지만 문서는 정확하게 쓰지 않는다면 거짓말을 할 수 있고 이것은 천 배나 더 나쁜 일이다.

책임감 있는 프로그래머는 이 사실을 가볍게 받아들이지 않을 것이다. 보면 바로 알 수 있는 코드를 어떻게 작성할 것인가? 그것이 과연 무슨 뜻인가? 바로 이런 뜻이다.
  • 누군가 읽을 것이라는 사실을 염두에 두고 코드를 작성하기
  • (앞에서 말한) 황금률을 적용하기
  • 복잡하지 않은 해법을 선택하기 (다른 해법이 용케 더 빠르다 해도)
  • 코드를 어지럽게 하는 사소한 최적화 시도는 포기하기
  • 코드를 읽을 사람을 생각하면서 그 사람이 쉽게 이해할 수 있게 하기 위해 시간을 할애하기
  • "foo", "bar", "doIt" 같은 함수명은 절대 쓰지 않기! 

2.2.6 형편없는 코드를 가지고 작업하기 

다른 사람이 작성한 질이 떨어지는 코드를 가지고 작업해야 하는 경우가 많다. 하지만, 신발을 신고 걸어 보기까지는 그 신발이 아주 형편없다고 생각하지는 말라. 그 사람이 일정의 압박에 맞추기 위해 어떤 일을 빨리 끝내도록 독촉을 받았을 수 있다. 어떻든 간에, 불분명한 코드를 가지고 작업을 하려면 그것을 이해해야 한다. 그 코드를 이해하자면 시간이 걸릴 것이고, 그 시간은 정해진 일정의 어디에선가 빼 와야 할 것이므로, 이 사실을 분명히 해야 한다. 소스 코드를 이해하기 위해서는 그것을 읽어 볼 수밖에 없다. 아마도 그것을 가지고 실험을 해 봐야 할 것이다.

비록 자기 자신만을 위한 것이라 해도, 코드에 대한 문서화 노력을 통해 생각해 보지 못했던 각도에서 그 코드를 생각해 볼 수 있으므로, 지금이 문서화하기 좋은 시간이며, 그 결과로 나온 문서는 유용할 것이다. 이렇게 하는 동안 그 코드의 일부나 전체를 재작성하려면 무엇이 필요할 것인지에 대해 생각해 보라. 그것을 재작성하는 것이 실제로 시간을 아끼는 일이 될 것인가? 코드를 재작성한다면 더 믿을 만하게 될 것인가? 이 때에는 자만심을 주의하도록 하라. 코드를 재작성한다면, 그것을 검사해야 하는 부담은 얼마나 될 것인가? 얻을 수 있는 이득과 비교해 볼 때 정말로 재검사할 필요가 있는 것인가?

자기가 작성하지 않은 코드에 대한 작업 시간을 추정할 때, 그 코드의 품질에 따라 문제나 예측불허의 것이 생길 위험 가능성에 대한 인식이 달라진다.

깔끔하지 못한 코드에는 프로그래머의 최고의 도구 두 가지, 즉 추상화와 캡슐화가 특히 잘 적용된다는 사실을 잘 기억해 두라. 코드의 큰 부분을 재설계할 수는 없겠지만, 어느 정도 추상화를 해 줄 수 있다면, 전체를 재작업하지 않고서도 좋은 설계가 주는 이득을 어느 정도는 얻을 수 있을 것이다. 특히 안 좋은 부분은 다른 부분에서 떼어 놓아 독립적으로 재설계할 수 있도록 해 볼 수도 있다.

2.2.7 소스 코드 제어 시스템을 이용하는 방법 

소스 코드 제어 시스템은 프로젝트를 효과적으로 관리할 수 있게 해 준다. 이 시스템은 개인에게도 유용하고 그룹에게는 필수적이다. 이 시스템은 여러 버전의 모든 변경 사항을 추적하므로 어떤 코드도 없어지지 않으며 변경 사항들의 의미를 기록해 놓을 수 있다. 소스 코드 제어 시스템이 있으면 소스 코드의 일부를 버리거나 디버그 코드를 넣는 일을 자신 있게 할 수 있다. 변경한 코드는 팀과 공유하거나 배포할 공식 코드와 잘 분리되어 보존되기 때문이다.

나는 뒤늦게야 소스 코드 제어 시스템의 이득을 알게 되었지만, 이제는 혼자 하는 프로젝트라 해도 그것 없이는 살 수 없을 것 같다. 일반적으로 이 시스템은 동일한 코드를 놓고 팀으로 작업할 때 필요하다. 하지만 이 시스템은 또 다른 큰 장점이 있다. 즉 이것을 통해 소스 코드를 성장하는 유기체로 인식하게 해 준다는 점이다. 변경된 것마다 새 이름이나 번호를 붙여 새로운 개정판으로 표시하기 때문에, 소프트웨어가 눈에 보이게 점진적으로 향상되어 간다고 생각하게 되는 것이다. 이것은 특히 초보자들에게 유용하다고 생각된다.

소스 코드 관리 시스템을 잘 이용하는 방법 중 하나는, 항상 최신의 상태를 유지하면서 며칠 동안 가만히 있는 것이다. 며칠 안에 마무리할 수 없는 코드는 체크인 상태로 있지만, 그것은 활성화되지 않고 호출되지 않을 상태로 있거나, 그 자신에서 갈라져 나온 분기(branch) 위치에 있을 것이므로, 다른 누구에게 어떠한 문제도 일으키지 않을 것이다. 실수가 있는 코드를 올려서 팀 동료들의 작업을 더디게 하는 것은 심각한 오류이다. 이것은 언제나 금기 사항이다.

2.2.8 단위별 검사를 하는 방법 

단위별 검사, 즉 코드로 만든 기능의 각 부분을 그것을 작성한 팀에서 검사하는 것은 코딩의 일부이지, 코딩과 다른 무엇이 아니다. 코드를 어떻게 검사할지 설계하는 것도 코드 설계의 한 부분이다. 비록 한 줄이라 해도 검사 계획을 기록해 놓아야 한다. 때때로 그 검사는 다음과 같이 단순할 것이다. "이 버튼이 좋아 보이는가?" 때로는 다음과 같이 복잡할 수도 있다. "이 정합(matching) 알고리듬은 틀림없이 정확한 짝을 찾아낼 것인가?"

할 수 있다면 검증 조건(assertion) 확인 방법이나 검사 자동화 도구(test driver)를 사용하라. 이 방법은 버그를 일찍 잡을 수 있게 해 줄 뿐만 아니라, 나중에도 유용하게 쓰일 수 있으며, 이렇게 하지 않았더라면 한참 고민하게 되었을 애매한 문제들을 줄일 수도 있을 것이다.

익스트림 프로그래밍(Extreme Programming) 기법을 사용하는 개발자들은 단위별 검사를 최대한 효과적으로 활용하여 코드를 작성한다. 이 작성 방법을 추천하는 것만큼 좋은 일도 없을 것이다.

2.2.9 막힐 때는 잠깐 쉬어라 

막힐 때는 잠깐 쉬어라. 나는 막힐 때는 15분 정도 명상을 하곤 한다. 그러면 다시 문제로 돌아왔을 때 그것이 마술같이 해결되곤 한다. 규모가 클 경우에는 하룻밤 잘 자는 것이 같은 효과를 내기도 한다. 잠시 다른 활동을 하는 것이 효과적일 수도 있다.

2.2.10 집에 갈 시간을 인지하는 방법 

컴퓨터 프로그래밍은 문화라고 할 만한 활동이다. 불행한 것은, 이것이 정신적, 신체적 건강을 그렇게 중요하게 생각하지 않는 문화라는 사실이다. 문화적이고 역사적인 이유 (예를 들어, 컴퓨터가 쉬는 밤중에 작업할 필요) 때문에, 그리고 저항할 수 없는 출시 일정의 압박과 프로그래머의 부족 때문에 컴퓨터 프로그래머는 전통적으로 초과 근무를 해 왔다. 소문으로 듣는 모든 이야기를 믿을 것이라고 생각하지는 않지만, 주당 60시간 근무는 일상적이며, 50시간은 상당히 적은 편에 속한다. 즉, 이보다 더 많은 시간이 요구되는 경우가 있다는 말이다. 이것은 좋은 프로그래머에게는 심각한 문제이다. 그는 자기 자신만 아니라 자기 팀 동료들도 책임지고 있기 때문이다. 자기가 집에 갈 시간, 때로는 다른 사람을 집에 보낼 시간도 인지하고 있어야 한다. 아이를 키우는 불변의 법칙이 없듯이, 이 문제를 해결할 불변의 법칙은 없다. 모든 사람은 서로 다르기 때문이다.

주당 60시간 이상 일하는 것은, 짧은 기간 (한 주 정도) 동안이나 해 볼 수 있을 정도로, 내게는 엄청난 노력이 필요하지만, 때로는 그렇게 해야 할 때가 있다. 한 사람에게 60시간 동안 일을 하게 하는 것이 공정한 것인지는 잘 모르겠다. 사실 40시간이 공정한 것인지도 잘 모르겠다. 하지만 분명한 것은, 초과 근무하는 시간 동안 별로 얻는 것 없이 오래 일하기만 하는 것은 어리석은 일이라는 사실이다. 나에 대해 말하자면, 주당 60시간 이상 일하는 것이 그렇다. 개인적으로는, 프로그래머는 고귀한 의무(noblesse oblige)를 다해야 하고 무거운 짐을 져야 한다고 생각한다. 하지만 봉이 되는 것은 프로그래머의 의무가 아니다. 그런데 슬프게도 프로그래머들은, 경영진의 눈에 들기 위해 애쓰는 관리책임자 같은 이들을 위해 재주를 부리는 곰이 되는 경우가 있다. 프로그래머들은, 다른 사람들을 기쁘게 하고 싶고, 싫다는 말을 잘 못하기 때문에, 종종 이런 요구에 굴복한다. 이를 대처하기 위한 네 가지 방어법이 있다.
  • 회사의 모든 사람들과 최대한 많이 대화하여 아무도 무슨 일이 일어나고 있는지에 대해 경영진을 현혹하지 못하게 하라.
  • 시간을 추정하고 일정을 잡을 때 방어적이고 명백하게 하여, 모든 사람들이 일정이 어떻게 되고 현재 어디쯤 가고 있는지 잘 볼 수 있게 하라.
  • 요구를 거부하는 법을 배우고, 필요하다면 팀이 함께 거부하도록 하라.
  • 어쩔 수 없다면 회사를 그만두라. 

많은 프로그래머들이 좋은 프로그래머이고, 좋은 프로그래머는 많은 것을 이루길 원한다. 이를 위해 그들은 시간을 효과적으로 관리해야 한다. 어떤 문제에 대해 생각을 가다듬고 거기에 깊이 몰두하게 될 때까지는 상당한 수고가 필요하다. 많은 프로그래머들은 생각을 가다듬고 몰두할 수 있는, 방해받지 않는 긴 시간이 있을 때 가장 잘 일할 수 있다고 한다. 하지만 사람들은 잠도 자고 다른 의무들도 이행해야 한다. 모든 사람들은 자신의 인간적 리듬과 업무적 리듬을 모두 만족시키는 방법을 찾아야 한다. 모든 프로그래머는, 아주 중요한 회의에만 참석하고 나머지는 일에 집중하는 날들을 확보하는 것과 같이, 효율적인 업무 기간을 획득하기 위해 최선을 다해야 한다.

나는 아이들이 있기 때문에 가끔이라도 아이들과 저녁 시간을 보내기 위해 노력한다. 나에게 가장 잘 맞는 리듬은, 하루 날 잡아 오래 일하고, 사무실이나 사무실 부근에서 잠을 잔 다음 (나는 집에서 직장까지 통근 시간이 길다), 일찍 집에 가서 아이들이 잠자리에 들기 전까지 시간을 보내는 것이다. 이것이 편안하지는 않지만, 여태껏 시험해 본 최선의 타협점이었다. 전염성 있는 병에 걸렸다면 집에 가라. 죽고 싶다는 생각이 든다면 집에 가야 한다. 몇 초 이상 누군가를 죽이고 싶다는 생각이 든다면 집에 가서 쉬어야 한다. 누군가가 가벼운 우울증을 넘어서 심각한 정신 이상이나 정신병의 증세를 보인다면 집에 가게 해야 한다. 피로 때문에 평소와 달리 부정직하거나 남을 속이고 싶다는 유혹이 든다면 쉬어야 한다. 피로와 싸우기 위해 마약이나 각성제를 쓰지 말라. 카페인을 남용하지도 말라.

2.2.11 까다로운 사람들과 상대하는 방법 

까다로운 사람들과 상대해야 하는 일이 있을 것이다. 자기 자신이 까다로운 사람일 수도 있다. 나 자신이 같이 일하는 사람들이나 권위 있는 인물들과 수시로 충돌하는 유형의 사람이라면, 여기에서 볼 수 있는 독립심은 소중하게 여겨야 할 것이나, 자신의 지성이나 원칙들을 희생하지 않는 범위 내에서 인간관계의 기능도 길러야 할 것이다.

이런 종류의 일을 겪어 보지 않았거나, 지금까지 살면서 직장 생활에는 별로 쓸모없는 행동 양식만 익혀 온 프로그래머들에게는 이것이 짜증스러운 일이 될 수 있다. 까다로운 사람들은 대개 반대 의견에 단련되어 있고 다른 사람들과 타협해야 한다는 사회적 압력에 별로 영향을 받지 않는다. 이 때 열쇠는 그 사람들을 적당히 존중해 주는 것이다. 이것은 내가 하고 싶은 것 이상으로 해 주는 것이지만, 그 사람들이 원하는 만큼은 안 될 것이다.

프로그래머들은 팀으로 함께 일해야 한다. 의견 불일치가 생기면, 어떻게든 해결해야 한다. 무작정 피할 수는 없는 노릇이다. 까다로운 사람들은 종종 매우 똑똑하고 쓸 만한 이야기를 하기도 한다. 까다로운 사람에게 편견 없이 귀를 기울이고 이해해 주는 것은 매우 중요하다. 대화 단절은 의견 불일치의 기반이 되지만, 강한 인내심으로 극복할 수 있는 경우도 있다. 대화가 산뜻하고 정감 있게 이뤄지도록 노력하고, 의견 충돌을 일으킬 만한 논쟁에 말려들지 말라. 이해하려고 노력할 만큼 한 뒤에는 결단을 내리라.

으스대는 사람의 강요 때문에 동의하지도 않는 일을 하지는 말라. 자신이 팀장이라면 최선이라고 생각하는 일을 하라. 개인적 이유로 결정을 내리지 말고, 자기가 결정한 근거를 설명할 준비를 해 두라. 까다로운 사람이 팀장이라면, 그의 결정이 개인적으로 영향을 미치지 않도록 하라. 일이 자기 방식대로 진행되지 않아도, 그 다른 방식에 따라 마음을 다해 일하라.

까다로운 사람들도 변하며 나아지기도 한다. 내 눈으로 직접 본 적도 있지만, 그렇게 흔하지는 않다. 어쨌든 모든 사람은 수시로 오르락내리락하기 마련이다.

모든 프로그래머, 특히 팀장들이 대면해야 하는 도전들 중 하나는 까다로운 사람을 전적으로 몰두하게 하는 것이다. 그런 사람들은 다른 사람에 비해서 일의 책임을 피하거나 수동적으로 저항하는 경향이 크다.

3 중급자 

3.1 개인적 기능들 

3.1.1 의욕을 계속 유지하는 방법 

프로그래머들이 아름답고 유용하고 멋진 것을 만들고 싶어 하는 의욕이 매우 크다는 사실은 훌륭하고도 놀라운 일이다. 이러한 욕구는 프로그래머에게만 있는 것도 아니고 보편적인 것도 아니지만, 이것은 프로그래머들 사이에 매우 강하고 일반적이어서 다른 일을 하는 사람들과 구분이 된다.

이것은 실제로 중요한 결과를 낳는다. 프로그래머에게 아름답지도 유용하지도 멋지지도 않은 일을 시키면 그들은 사기가 떨어진다. 너저분하고 멍청하고 지루한 일을 해서 돈을 많이 벌기도 한다. 하지만 결국에는 재미있게 하는 일이 회사에 큰 돈을 벌어다 준다.

분명히 의욕을 불러일으키는 기법들을 중심으로 조직된 모든 산업 분야에서 여기에 적용되는 것들이 있다. 프로그래밍에 해당한다고 인정할 만한 것들은 다음과 같다.
  • 그 일을 자랑스럽게 이야기한다.
  • 새로운 기법, 언어, 기술을 적용할 기회를 찾는다.
  • 각 프로젝트에서 아무리 작더라도 무엇인가를 배우거나 가르치려고 노력한다. 

끝으로, 할 수 있다면, 개인적으로 의욕을 얼마나 고취시키는가에 따라 자신의 일의 영향력을 가늠해 보라. 예를 들어, 버그를 고칠 때, 내가 버그를 몇 개나 고쳤는지 세는 것으로 의욕이 생기지는 않는다. 지금까지 고친 버그의 개수가 아직 남아 있는 버그의 개수와 무관하며, 그 개수는 회사의 고객들에게 기여하는 전체 가치에서 아주 사소한 부분을 차지할 뿐이기 때문이다. 하지만, 버그 하나마다 고객 한 사람이 기뻐한다고 생각하면 그것은 개인적으로 의욕이 생기게 한다.

3.1.2 널리 신뢰받는 방법 

신뢰받기 위해서는 신뢰받을 만해야 한다. 또한 활동이 두드러져야 한다. 자신에 대해 아무도 알지 못한다면, 아무런 신뢰도 받을 수 없을 것이다. 팀 동료처럼 자신과 가까운 사람들과 같이 있을 때는 이것이 큰 문제가 아닐 것이다. 신뢰는 자기 부서나 팀이 아닌 사람들에게 응답하고 지식을 줌으로써 쌓아간다. 때로는 이러한 신뢰를 악용하여 불합리한 부탁을 하는 사람도 있다. 이럴 때에는 걱정하지 말고, 그 부탁을 들어주자면 자신이 무슨 일을 포기해야 하는지 설명하면 된다.

모르는 것을 아는 체하지 말라. 팀 동료가 아닌 사람들에게는 "머리에서 맴돌면서 기억나지 않는 것"과 "전혀 알 수 없는 것"을 명확히 구분해야 할 것이다.

3.1.3 시간과 공간 사이에서 균형을 잡는 방법 

대학에 가지 않아도 좋은 프로그래머는 될 수 있지만, 기초적인 계산 복잡도 이론을 모른다면 좋은 중급 프로그래머는 될 수 없다. O("big O") 표기법을 알 필요는 없지만, "상수 시간", "n log n", "n 제곱"의 차이는 이해할 수 있어야 한다. 이런 지식이 없어도 시간과 공간 사이에서 균형을 잡는 방법을 직관으로 알고 있을 수 있지만, 그런 지식이 없다는 것은 동료들과 대화할 때 필요한 튼튼한 기초가 없는 것과 같다.

알고리듬을 설계하거나 이해할 때, 그것을 실행하는 데 걸리는 시간은 입력 값의 크기의 함수인 경우가 있다. 이 때, 알고리듬의 실행 시간이 (변수 n으로 표현되는) 그 크기와 그 크기의 로그값의 곱에 비례하면, 그 최악(또는 기대되는, 또는 최선)의 실행 시간이 "n log n"이라고 말할 수 있다. 이 표기법과 말하는 방식은 자료 구조가 차지하는 공간에도 마찬가지로 적용될 수 있다.

내게는, 계산 복잡도 이론이 물리학만큼이나 아름답고 심오하게 보인다. (조금 딴 길로 샌 것 같다!)

시간(프로세서 속도)과 공간(메모리)은 서로 균형을 맞출 수 있다. 공학이란 타협에 대한 것이며, 이것은 아주 좋은 예가 된다. 이것은 항상 체계적인 것은 아니다. 일반적으로 꽉 조이는 인코딩을 통해 공간을 절약할 수 있지만, 그것을 디코딩해야 할 때는 계산 시간이 더 많이 걸릴 것이다. 캐쉬를 사용함으로써, 즉 가까이에 복사본을 저장할 공간을 사용함으로써 시간을 절약할 수 있지만, 캐쉬의 내용을 일관되게 유지하고 있어야 할 것이다. 자료 구조에 더 많은 정보를 담음으로써 시간을 절약할 수 있는 경우가 있다. 이것은 대개 적은 공간을 차지하지만 알고리듬이 복잡해질 수 있다.

공간이나 시간의 균형 관계를 개선하는 것은 종종 다른 쪽이 극적으로 변하게 할 수 있다. 하지만 이 작업을 하기 전에 지금 개선하려고 하는 것이 정말로 그런 개선이 필요한 것인지 스스로 물어야 한다. 알고리듬을 가지고 작업하는 것은 재미있는 일이지만, 아무 문제가 없는 것을 개선하려는 것은 눈에 띌 만한 차이는 못 내면서 검사할 짐만 늘어나게 할 것이라는 냉엄한 사실에 대해 눈이 가려지지 않도록 조심해야 한다.

최신 컴퓨터의 메모리는, 프로세서 시간과 달리 벽에 부딪히기 전까지는 그것이 어떻게 쓰이는지 볼 수 없기 때문에, 값싼 것처럼 보인다. 하지만 문제가 생기기라도 하면 그것은 재앙이 된다. 메모리를 사용하는 데에는, 메모리에 상주해야 하는 다른 프로그램에 미치는 영향이나, 메모리를 할당하고 해제하는 데 드는 시간 등의 숨어 있는 비용이 있다. 속도를 얻으려고 공간을 써 버리기 전에 이 사실을 주의 깊게 생각하라.

3.1.4 압박 검사를 하는 방법 

압박 검사(stress test)는 재미있다. 처음에는 압박 검사의 목적이 시스템에 부하가 걸려도 잘 동작하는지 알아보는 것으로 보인다. 실제로는 시스템에 부하가 걸려도 잘 동작하지만 부하가 아주 클 때는 어떤 방식으로든 동작이 멈추는 경우가 대부분이다. 나는 이것을 벽에 부딪침 또는 탈진이라고 부른다. 예외가 있기도 하지만, 거의 항상 '벽'은 있다. 압박 검사의 목적은 그 벽이 어디에 있는지 알아보는 것이며, 그 벽을 얼마나 더 밀어낼 수 있을지 알아보는 것이다.

압박 검사 계획은, 그것을 통해 프로젝트에서 기대하는 바가 무엇인지 명확해지기 때문에, 프로젝트 초기에 세워야 한다. 웹 페이지 요청에 2초가 걸리는 것은 비참한 실패일까, 대단한 성공일까? 동시 사용자 500명은 충분한가? 이것은 물론 상황에 따라 다르지만, 그 요구에 부응하는 시스템을 설계할 때 그 답을 알고 있어야 한다. 압박 검사는 실제 상황을 충분히 쓸 만하게 본떠서 해야 한다. 동시에 시스템을 사용하면서 오류를 일으키거나 무엇을 할지 예측할 수 없는 500명의 사람들을 쉽게 흉내 내는 일이 실제로는 불가능하지만, 500개의 모의실험을 만들어 사람들이 어떻게 행동할지 본뜨게 해 보는 정도는 할 수 있을 것이다.

압박 검사를 할 때는 가벼운 부하에서 시작해서, 입력 비율이나 입력 크기와 같은 어떤 범위에 따라 벽에 부딪칠 때까지 부하를 늘려간다. 벽이 요구 조건을 만족하기에 너무 가깝다면, 어떤 자원이 병목이 되어 있는지 알아내라. (대개 주된 원인이 있기 마련이다.) 그것이 무엇 때문인가? 메모리? 프로세서? I/O? 네트워크 대역폭? 데이터 쟁탈? 그 후에는 그 벽을 어떻게 움직일 수 있을지 알아내라. 벽을 움직이는 것, 즉 시스템이 견딜 수 있는 최대 부하를 증가시키는 것이 부하가 적은 시스템의 성능에 도움이 되지 않거나 오히려 해가 될 수도 있다는 사실은 기억하라. 보통은 큰 부하가 걸렸을 때의 성능이 적은 부하가 걸렸을 때의 성능보다 중요하다.

머리 속으로 모형을 그릴 수 있도록 여러 가지 다른 차원들도 살필 수 있어야 한다. 한 가지 기법만으로는 충분하지 않다. 예를 들어, 로그 기록은 시스템에서 일어난 두 사건 사이의 벽시계 시간에 대해 잘 알 수 있게 해 주지만, 그것이 잘 구성되어 있지 않은 한, 메모리 사용이나 자료 구조의 크기에 대해서는 살펴볼 수 없다. 같은 원리로, 현대적인 시스템에서는 많은 컴퓨터와 많은 소프트웨어 시스템들이 서로 협력하여 동작하는데, 특히 벽에 부딪쳤을 때 (즉, 성능이 입력 크기에 비례하지 않게 될 때) 이 다른 소프트웨어 시스템들이 병목이 될 수 있다. 이 시스템들을 살펴볼 수 있는 것은, 모든 관련 장비들의 프로세서 부하만 측정할 수 있다 해도, 큰 도움이 될 수 있을 것이다.

벽이 어디에 있는지 아는 것은, 벽을 옮기는 것뿐만 아니라 사업을 효과적으로 관리할 수 있도록 예측 가능성을 마련해 주는 데에도 필수적이다.

3.1.5 간결성과 추상성의 균형을 잡는 방법 

추상성은 프로그래밍의 열쇠이다. 얼마나 추상화해야 할지는 주의해서 선택해야 한다. 초보 프로그래머들은 열심이 지나쳐서 실제로 필요 이상으로 추상화하는 경우가 있다. 이것의 징조 중 하나는 아무 코드도 들어 있지 않으면서, 다른 것을 추상화하는 것 외에 아무 일도 하지 않는 클래스를 만드는 것이다. 이런 것의 매력은 이해할 만하지만 코드 간결성의 가치도 추상성의 가치에 비교하여 재 봐야 한다. 때때로 열정적인 이상주의자들이 저지르는 실수를 보게 된다. 즉 프로젝트 초기에 수많은 클래스들을 정의하면서 그것을 통해 추상성을 멋지게 달성하고, 발생할 수 있는 모든 사태를 다룰 수 있을 것으로 기대한다. 그런데 프로젝트가 진행되고 피로가 쌓임에 따라 코드 자체가 너저분해진다. 함수의 몸체는 되어야 할 것 이상으로 길어진다. 비어 있는 클래스들은 문서화하는 데 짐이 되고 압박이 오면 결국 잊혀진다. 추상화에 쏟은 에너지가 프로그램을 간결하고 단순하게 하는 데에 쓰였다면 최종 결과가 더 좋아졌을지도 모른다. 이것은 사색적인(speculative) 프로그래밍의 전형이다. 나는 폴 그레이엄(Paul Graham)의 "간결함이 힘이다(Succinctness is Power)"라는 글을 강력 추천한다. <PGSite>

정보 은닉이나 객체 지향 프로그래밍과 같은 유용한 기법들을 지나치게 사용하면서 독단적 견해가 생기기도 한다. 이 기법들은 코드를 추상화하게 하고 변화를 예측하게 한다. 하지만 개인적인 생각으로는 너무 사색적인 코드를 작성하지 않는 것이 좋다. 예를 들어, 어떤 객체의 정수 변수를 변경자(mutator)와 접근자(accessor) 뒤에 숨겨서 변수 자체는 드러나지 않고 작은 접점(interface)만 드러나게 하는 것은 받아들일 만한 스타일이다. 이를 통해서, 호출하는 코드에 영향을 주지 않으면서 그 변수의 구현 방식을 바꿀 수 있으며, 이것은 매우 안정된 API를 내놓아야 하는 라이브러리 제작자에게 적합할 것이다. 하지만 나는, 호출하는 코드를 우리 팀이 소유하고 있어서 호출되는 쪽만큼 호출하는 쪽도 다시 코딩할 수 있다면, 코드가 장황해지는 것을 감수할 만큼 이것이 이득이 된다고 생각하지는 않는다. 이런 사색적 프로그래밍으로 얻는 이득을 따져 보면 코드 너덧 줄 늘어나는 것도 아깝다.

이식성(portability)에 대해서도 비슷한 문제를 볼 수 있다. 모든 코드는 다른 컴퓨터, 컴파일러, 소프트웨어 시스템, 플랫폼에 바로 혹은 아주 쉽게 이식될 수 있어야 하는가? 나는 이식성은 없지만 간결하면서 쉽게 이식할 수 있는 코드가, 장황하면서 이식성 있는 것보다 낫다고 생각한다. 특정 DBMS에 맞는 데이터베이스 질의를 하는 클래스와 같이 이식성 없는 코드를 정해진 영역에 한정해 놓는 것도 비교적 쉬우면서 분명히 좋은 생각이다.

3.1.6 새로운 기능을 배우는 방법 

새로운 기능, 특히 기술과 무관한 것을 배우는 것은 무엇보다 재미있는 일이다. 이것이 프로그래머들의 의욕을 얼마나 많이 불러일으키는지 이해하는 회사들은 사기가 더욱 높아질 수 있을 것이다.

인간은 행함으로 배운다. 책 읽기와 수업 듣기가 유용한 것은 틀림없지만, 아무 프로그램도 작성해 보지 않은 프로그래머를 인정할 수 있겠는가? 어떤 기능이든 배우기 위해서는 그 기능을 스스럼없이 연습해 볼 수 있어야 할 것이다. 새로운 프로그래밍 언어를 배울 때에는, 큰 프로젝트를 해야 하게 되기 전에 그 언어를 사용하는 작은 프로젝트를 해 보라. 소프트웨어 프로젝트 관리를 배울 때에는, 작은 프로젝트를 먼저 관리해 보도록 하라.

좋은 사수(mentor)가 있다고 해서 스스로 하는 것을 소홀히 해서는 안 되겠지만, 사실 책 한 권보다는 훨씬 낫다. 사수의 지식을 전수받는 대신 그들에게 무엇을 해 줄 수 있을까? 최소한 그들의 시간이 낭비가 되지 않도록 열심히 공부해 줄 수는 있을 것이다.

상사에게 공식 연수를 받게 해 달라고 요청해 보라. 하지만 이것이 그 시간 동안 배우고 싶은 새로운 기술을 가지고 놀아 보는 것보다 별로 나을 것이 없는 경우도 있다는 것을 알아 두라. 그렇다 해도, 우리의 불완전한 세상에서는 공식 연수에서 저녁 식사 파티를 기다리며 강의 내내 잠만 자는 경우가 많음에도 불구하고 노는 시간보다는 연수를 요청하기가 쉬울 것이다.

팀장이라면, 팀원들이 어떻게 배우는지 이해하고 그들에게 관심 있는 기능을 연습할 수 있는 적절한 규모의 프로젝트를 할당함으로써 그들을 도와주라. 프로그래머에게 가장 중요한 기능은 기술에 관한 것이 아니라는 사실을 잊지 말라. 팀원들에게 용기와 정직성과 의사소통 능력을 시험하고 실행할 수 있는 기회를 주라.

3.1.7 타자 연습 

자판을 보지 않고 타자할 수 있도록 연습하라. 이것은 중급 기능이다. 왜냐하면, 코드 작성은 어려운 일이므로, 아무리 타자를 잘 한다 해도, 거기에는 타자 속도가 별 의미가 없고, 코드 작성에 걸리는 시간에 별 영향을 주지도 않기 때문이다. 하지만, 중급 프로그래머가 되면 동료나 다른 사람들에게 일반 언어로 글을 쓰는 데 많은 시간을 보내게 될 것이다. 이것은 자신의 헌신성에 대한 재미있는 시험이다. 그런 것을 배우는 일이 그렇게 즐겁지는 않지만 어쨌든 시간을 바쳐야 한다. 마이클 티먼(Michael Tiemann)이 MCC에 있을 때 사람들이 그의 방문 밖에 서서, 엄청나게 빠른 타자 속도 때문에 자판이 윙윙거리는 소리를 듣곤 했다는 전설이 있다. 주: 지금 현재 그는 레드햇(RedHat)의 최고 기술 책임자(CTO)이다.

3.1.8 통합 검사를 하는 방법 

통합 검사(integration testing)는 단위별 검사를 마친 여러 구성요소들을 통합하는 검사이다. 통합은 비싼 대가를 치르고 얻게 되며, 그것은 이 검사를 통과함으로써 얻게 된다. 시간 추정과 일정 계획에 이 검사를 포함시켜야 한다.

이상적으로는 마지막 단계에서 별도로 통합을 실시하지 않아도 되도록 프로젝트를 조직해야 할 것이다. 프로젝트가 진행되는 과정에서 각 요소들이 완성되어 가면서 점진적으로 그것들을 통합하는 것이 훨씬 낫다. 별도의 통합 과정을 피할 수 없다면 주의해서 시간을 추정하라.

3.1.9 의사소통을 위한 용어들 

프로그래밍 언어는 아니지만 의사소통을 위해 공식적으로 정의된 문법 체계에 따른 용어들이 있다. 이 용어들은 특별히 표준화를 통해 의사소통을 돕기 위해 만들어진 것이다. 2003년 현재, 이런 용어들 중 가장 중요한 것으로 UML, XML, SQL이 있다. 이것 모두에 대해서는, 제대로 의사소통하고 언제 그 용어를 사용할지 결정할 수 있기 위해, 어느 정도 친숙해질 필요가 있다.

UML은 설계에 대해 설명하는 도형을 그리기 위해 풍부하게 갖춰진 형식 체계이다. 시각적이고 형식적이라는 점에서 일종의 미학(beauty lines)이라고 할 수 있다. 저자와 독자가 모두 UML을 안다면 많은 양의 정보를 쉽게 전달할 수 있다. 설계 내용에 대해 의사소통할 때 UML을 사용하는 경우가 있으므로 그것을 알 필요가 있다. UML 도형을 전문적으로 그려 주는 유용한 도구들이 있다. 많은 경우에 UML는 너무 형식적이어서, 나는 설계 내용을 도형으로 나타내기 위해 단순한 상자와 화살표 형식을 사용하곤 한다. 하지만 나도 UML이 최소한 라틴어를 공부하는 것만큼 도움이 된다는 사실은 어느 정도 확신한다.

XML은 새로운 표준을 정의하기 위한 표준이다. 간혹 XML이 데이터의 상호교환 문제에 대한 해결책인 것처럼 소개되기도 하지만 사실 그런 것은 아니다. 그것보다는, 데이터의 상호교환에서 가장 따분한 부분, 즉 특정 방식으로 표현된 데이터를 선형의 구조로 나열하고, 그것을 다시 원래의 구조로 파싱하는 작업을 기특하게도 자동화하는 것이다. XML은, 비록 아직 필요한 것들의 일부만 구현되긴 했어도, 훌륭하게 자료형 검사와 정확성 검사를 한다.

SQL은, 완전한 프로그래밍 언어는 아니지만, 매우 강력하고 풍부한 데이터 질의와 처리 언어이다. 이것은 다양한 종류가 있으며, 특히 제품에 크게 의존하지만 표준화된 핵심 부분보다는 덜 중요하다. SQL은 관계형 데이터베이스의 공통 언어이다. 관계형 데이터베이스를 이해하는 것이 득이 되는 분야에서 일할 수도 있고 그렇지 않을 수도 있지만, SQL과 그 문법에 대해 기초적인 것은 알아둘 필요가 있다.

3.2 팀의 기능들 

3.2.1 개발 시간을 관리하는 방법 

개발 시간을 관리하기 위해서는, 프로젝트 계획서가 간결하고 신선(up-to-date)하도록 하라. 프로젝트 계획서에는 시간 추정, 작업 일정, 진척 상황을 표시하기 위한 진도표(milestones), 시간 추정된 각 과제에 대한 팀이나 자기 자신의 시간 할당 등이 들어 있다. 여기에는 품질보증팀 사람들과 회의, 문서 준비, 장비 주문과 같이 잊지 말고 해야 할 다른 일들도 포함된다. 팀으로 일하는 것이라면, 프로젝트 계획서는 프로젝트 시작부터 진행 과정 내내 팀 전체의 동의에 의한 것이어야 한다.

프로젝트 계획서는 의사결정을 돕기 위해 존재하는 것이지, 자신이 얼마나 조직적인 사람인지 보여 주기 위한 것이 아니다. 프로젝트 계획서가 너무 길고 오래되었다면 의사결정에 아무 소용이 없을 것이다. 실제로 이러한 의사결정은 개개인에 관한 것이다. 계획서와 판단력에 의해 과제를 이 사람에게서 저 사람에게로 넘길 것인지 결정해야 한다. 진도표는 진척 상황을 표시한다. 화려한 프로젝트 기획 도구를 사용한다면 그것으로 프로젝트에서 '개발 초기 대형 설계(Big Design Up Front)'를 하려는 유혹에 빠지지 말고, 간결함과 신선함을 위해 사용하도록 하라.

진도를 못 맞췄다면, 그 진도만큼 프로젝트 일정 완료가 늦어진다고 상사에게 알리는 등의 즉각적인 행동을 취해야 한다. 시간 추정과 작업 일정은 처음부터 완벽할 수는 없을 것이다. 이것은 진도를 못 맞춘 날들을 프로젝트 후반에 만회할 수 있을지도 모른다는 환상을 낳는다. 물론 그럴 수 있을지도 모른다. 하지만 이것은 그 부분을, 과대 추정할 수도 있었겠지만, 과소 추정했기 때문이다. 그러므로 좋듯 싫든 간에 프로젝트의 일정 완료는 이미 늦어진 것이다.

계획서에 다음의 시간들도 포함되도록 명심하라. 팀 내부 회의, 시연, 문서화, 일정에 따라 반복되는 활동들, 통합 검사, 외부인들 상대하기, 질병, 휴가, 기존 제품들의 유지, 개발 환경의 유지 등. 프로젝트 계획서는 외부인이나 상사에게 자신이나 자신의 팀이 무엇을 하고 있는지 보여 줄 수 있는 '중간' 다리가 될 수 있다. 이런 이유 때문에 계획서는 짧고 신선해야 한다.

3.2.2 타사 소프트웨어의 위험 부담을 관리하는 방법 

프로젝트 내에서 통제할 수 없는 다른 조직이 만든 소프트웨어에 의존해야 하는 경우가 있다. 타사 소프트웨어와 연관된 큰 위험 부담들은 관련된 모든 사람이 인식하고 있어야 한다.

절대, 절대로 거품(vapor)에 희망을 두지 말라. 거품이란, 나올 것이라고 광고는 하면서 아직 나오지 않은 소프트웨어를 말한다. 이것은 업계를 떠나게 될 가장 확실한 방법이다. 어떤 특징이 있는 어떤 제품이 어느 날에 출시될 것이라는 소프트웨어 회사의 약속을 의심스러워하기만 하는 것은 현명하지 않다. 그것을 완전히 무시하고 그 소식을 들었다는 것조차 잊어버리는 것이 더욱 현명하다. 회사에서 쓰이는 어떤 문서에도 그 소프트웨어에 대해 쓰지 않도록 하라.

타사 소프트웨어가 거품이 아니라면, 위험 부담은 여전히 있지만 그래도 최소한 손을 써 볼 수는 있을 것이다. 타사 소프트웨어의 사용을 고려하고 있다면, 일찍 에너지를 들여서 그것을 평가해야 한다. 사람들은 세 가지 제품이 적합한지 평가하는 데에 2주에서 2개월이 걸린다는 말을 듣고 싶어 하지 않겠지만, 최대한 일찍 그 일을 마쳐야 한다. 적절한 평가 없이는 통합(integration)에 드는 비용을 정확하게 추정할 수 없다.

특정한 목적에 기존의 타사 소프트웨어가 적합한지는 극소수의 사람들만 알고 있다. 그것은 매우 주관적이며 일반적으로 전문가들의 영역이다. 이런 전문가들을 찾을 수 있다면 많은 시간을 아낄 수 있다. 프로젝트가 타사 소프트웨어 시스템에 너무 완전히 의존해서 통합에 실패하면 프로젝트 자체가 실패하는 경우도 있을 수 있다. 그러한 위험 부담들을 작업 일정에 분명히 밝혀 두라. 쓸 수 있는 다른 시스템을 확보하는 등 만약의 사태에 대비한 계획도 세워 두고, 위험 부담을 일찍 제거할 수 없을 때 스스로 그 기능을 구현할 수 있는 능력도 키워 두라. 절대로 일정이 거품에 의존하지 않도록 하라.

3.2.3 컨설턴트를 관리하는 방법 

컨설턴트를 활용하되 그들에게 의지하지는 말라. 그들은 훌륭하고 많이 존중받을 만하다. 그들은 수많은 다양한 프로젝트들을 보아 왔기 때문에 특정한 기술들이나 프로그래밍 기법들에 대해 많이 알고 있는 경우가 있다. 그들을 활용하는 최선의 방법은, 사례를 중심으로 강의하는 사내 강사로서 활용하는 것이다.

하지만, 그들의 강점과 약점을 알 만한 충분한 시간이 없다고 해서 그들을 일반 직원들처럼 팀의 일원이 되게 할 수는 없다. 그들이 금전적 손실을 감수하는(financial commitment) 경우는 거의 없다. 그들은 아주 쉽게 떠난다. 회사가 잘 운영된다면 그들이 얻을 것은 별로 없을 것이다. 어떤 사람은 좋고, 어떤 사람은 그저 그렇고, 어떤 사람은 안 좋을 것이다. 컨설턴트를 선정할 때도 직원을 채용할 때처럼 주의를 기울이지 않는다면 좋은 사람을 만나기는 힘들 것이다.

컨설턴트가 코드를 작성해야 한다면, 계속해서 그 코드를 주의를 기울여 검토해야 한다. 검토해 보지 않은 코드가 뭉텅이로 있는 위험 부담을 안고서 프로젝트를 잘 마칠 수는 없을 것이다. 이것은 사실 모든 팀원들에게도 적용되는 사실이지만, 가까이에 있는 팀원들이야 잘 아는 사람들 아닌가.

3.2.4 딱 적당하게 회의하는 방법 

회의에 드는 비용을 잘 고려하라. 그 비용은 회의 시간에 참석 인원수를 곱한 값이다. 회의가 필요할 때도 있지만 규모가 작을수록 좋다. 의사소통의 질은 소규모 회의 때 더 좋고, 낭비되는 총 시간도 적다. 누가 회의 때 지루해하는 것 같으면, 이것은 회의 참석 인원을 줄여야 한다는 신호이다.

비공식적인 의사소통을 장려하기 위해서는 할 수 있는 일은 모두 해야 한다. 다른 시간들보다 동료들과 함께 하는 점심시간 동안 쓸만한 일들이 더 많이 이뤄진다. 그런데 이것을 인식하지도 않고 이 사실을 지지하지도 않는 회사가 더 많다는 것은 부끄러운 일이다.

3.2.5 무리 없이 정직하게 반대 의견을 내는 방법 

반대 의견은 의사 결정을 잘 하기 위해 꼭 필요한 것이지만, 조심스럽게 다뤄야 한다. 자기 생각을 적절히 표현했고 결정이 내려지기 전에 사람들이 그 생각에 귀를 기울였다고 스스로 느낀다면 좋겠다. 그 경우 더 이상 할 말은 없는 것이고, 이제는 결정된 일에 대해 반대했더라도 그것을 후원할 것인지 결정하면 된다. 반대했더라도 이제 그 결정을 따를 수 있다면 그렇게 하겠다고 말하라. 이것은 내가 생각이 있으며 무조건 찬성하는 사람이 아니지만, 결정된 것은 존중하며 팀의 일원으로 일한다는 자신의 가치를 보여 준다.

어떤 때는 결정 사안에 대해 반대했음에도, 의사결정권자들이 그 의견을 충분히 고려하지도 않고 결정을 내리기도 할 것이다. 이 때에는 문제 제기를 할 것인지 회사나 부족(tribe)의 차원에서 잘 따져 봐야 한다. 그 결정에 사소한 실수가 있는 것 같다면, 재고할 가치는 없을 것이다. 만약 그 실수가 크다면, 당연히 논쟁을 벌여야 한다.

보통은, 이것은 별 문제가 되지 않지만, 스트레스가 많은 상황이나 특별한 성격의 사람들에게는 이것이 개인적인 문제를 일으키기도 한다. 예를 들어, 일 잘 하는 어떤 프로그래머는 결정이 잘못되었다고 믿을 만한 충분한 이유가 있어도 그것에 도전할 자신감이 없다. 더 나쁜 상황이라면, 의사결정권자도 자신감이 없어서 그것을 자기 권위에 대한 개인적인 도전으로 받아들이기도 한다. 이런 경우에 사람들은 비열하게 머리를 써서 반응한다는 것을 꼭 기억할 필요가 있다. 논쟁은 남들이 없을 때 벌여야 하고, 새로운 사실을 알게 됨으로써 전에 내린 결정의 근거가 어떻게 달라지는지 잘 보여주도록 노력해야 한다.

그 결정이 번복되든 그렇지 않든, 그 대안은 충분히 검토되지 않았을 것이므로 나중에라도 '내 그럴 줄 알았어!' 하고 우쭐대지 않도록 하라.

3.3 판단 능력 

3.3.1 개발 시간에 맞춰 품질을 조절하는 방법 

소프트웨어 개발은 항상 프로젝트의 목적과 프로젝트의 마무리 사이에서 타협하는 일이다. 프로젝트 결과의 배치를 신속하게 하기 위해 공학적 혹은 사업적 감수성을 거스르면서까지 품질을 조절하라는 요구를 받을 수 있다. 예를 들어, 소프트웨어 공학적으로 형편없고 수많은 유지 보수 문제를 일으킬 것이 뻔한 일을 하도록 요구받을 수 있다.

이런 일이 일어난다면 우선 책임 있게 할 일은, 그 사실을 팀에 알리고 품질 저하에 따른 비용을 분명히 설명하는 것이다. 어쨌든, 상사보다는 그 사실에 대해 더 잘 이해하고 있어야 한다. 무엇을 잃을 것이고 무엇을 얻을 것인지, 또 이번에 잃은 것을 다음 단계에 만회하기 위해서는 어떤 비용을 감수해야 하는지 분명히 하라. 이 때, 잘 짜여진 프로젝트 계획서의 선명함이 도움이 될 것이다. 품질을 조절하는 것이 품질 보증의 노력에 영향을 준다면 (상사와 품질보증팀 사람들 모두에게) 그 사실도 지적하라. 품질 조절 때문에 품질 확인 과정을 거치면서 많은 버그가 발견될 것 같다면 그것도 지적하라.

그래도 요구가 계속된다면, 조잡해지는 것이 특정 부분에만 머물게 하여 다음 단계에 재작성이나 개선 계획을 세울 수 있도록 해야 한다. 이 사실을 팀에 알려서 그 계획을 세울 수 있게 하라.

슬래쉬닷(Slashdot)의 닌자프로그래머(NinjaProgrammer)는 이런 보석 같은 글을 보내 왔다.

설계가 좋으면 코드 구현이 나빠도 회복 가능성이 있다는 사실을 기억하라. 코드 전체에 인터페이스와 추상화가 잘 되어 있으면, 언젠가 있을 수 있는 코드 재작성의 고통도 훨씬 덜할 것이다. 코드를 명쾌하게 작성하기도 힘들고 고치기도 힘들다면, 이런 문제를 야기하는 핵심 설계에 무슨 문제가 없는지 생각해 보라. 

3.3.2 소프트웨어 시스템의 의존성을 관리하는 방법 

최신의 소프트웨어 시스템은 직접 통제할 수 없는 수많은 구성요소(component)들에 의존하는 경향이 있다. 이것은 상승효과(synergy)와 재사용을 통해 생산성을 높인다. 하지만, 각 요소들은 다음의 문제들을 수반한다.
  • 그 구성요소에 있는 버그는 어떻게 고칠 것인가?
  • 그 구성요소 때문에 특정 하드웨어나 소프트웨어 시스템만 사용해야 하는가?
  • 그 구성요소가 기능을 완전히 상실한다면 어떻게 할 것인가? 

그 구성요소를 어떻게든 캡슐화 하여 주변과 격리시키고 언제든 다른 것으로 교체할 수 있게 하는 것이 항상 최선이다. 그 요소가 전혀 동작하지 않는 것으로 판명된다면 다른 것으로 대체할 수도 있지만, 직접 작성해야 할 수도 있다. 캡슐화가 이식성(portability)과 같은 말은 아니지만, 쉽게 이식될 수 있게 하기 때문에 거의 그와 같다고 할 수 있다.

구성요소들의 소스 코드를 갖고 있다면 위험 부담이 네 배 정도는 줄어든다. 소스 코드가 있다면 그것을 평가하기도 쉽고, 디버그 하기도 쉽고, 임시방편을 찾기도 쉽고, 수정판을 만들기도 쉽다. 수정판을 만든다면, 그것을 그 구성요소의 소유자에게도 보내 줘서 공식 배포판에 반영하게 해야 한다. 그렇지 않으면 비공식판을 유지하는 불편을 감수해야 할 것이다.

3.3.3 소프트웨어의 완성도를 판단하는 방법 

다른 사람들이 작성한 소프트웨어를 사용하는 것은 견고한 시스템을 신속하게 완성하는 가장 효과적인 방법 중 하나이다. 그런 일을 망설일 필요는 없지만, 그에 연관된 위험 요소들을 검사해야 한다. 가장 큰 위험 부담 중 하나는 사용할 제품에 포함되어 사용되는 동안, 완성도를 갖추기 전의 소프트웨어에서 흔히 볼 수 있는 것처럼, 버그가 나타나거나 거의 작동 불능인 상태가 되는 것이다. 어떤 소프트웨어 시스템을 통합할 것인지 고려하기 전에, 그것이 사내에서 만들었든 타사에서 만들었든, 그것이 정말로 사용할 수 있을 만큼 충분한 완성도를 갖췄는지 고려하는 것은 매우 중요하다. 스스로 물어봐야 하는 열 가지 질문이 있다.
  1. 거품(vapor)은 아닌가? (약속만 있는 것은 완성도가 아주 떨어지는 것이다.)
  2. 그 소프트웨어에 대한 평판(lore)이 어떤지 알 수 있는가?
  3. 내가 첫 사용자인가?
  4. 개정판이 계속 나올 만한 충분한 동기(incentive)가 있는가?
  5. 유지 보수 노력이 계속되고 있는가?
  6. 현재 유지 보수 담당자가 없어도 사장되지 않을 것인가?
  7. 절반밖에 안 좋더라도 익숙한 다른 대안(seasoned alternatives)이 있는가?
  8. 부족(tribe)이나 회사에서 그것에 대해 알고 있는가?
  9. 부족이나 회사에서 추천할 만한가?
  10. 문제가 있어도 그것을 해결할 만한 사람을 채용할 수 있는가? 

이 항목들을 조금만 생각해 봐도 잘 만들어진 자유 소프트웨어나 공개 소프트웨어가 기업의 위험 부담을 줄이는 데에 얼마나 큰 가치가 있는지 알 수 있을 것이다.

3.3.4 구입과 개발 사이에서 결정하는 방법 

사업을 목적으로 한 회사나 프로젝트에서는 소프트웨어로 무엇인가를 달성하기 위해 노력하면서 빈번히 구입과 개발 사이에서 결정을 해야 한다. 이 단계에 왔다는 것은 두 가지 측면에서 불행한 일이다. 즉, 구입하지 않아도 되는 공개 소프트웨어, 자유 소프트웨어를 제쳐놓았기 때문일 것이고, 더 중요한 것은, 통합에 드는 비용도 고려해야 하기 때문에, 구입해서 통합하는 것과 직접 개발해서 통합하는 것 사이에서 결정하는 것을 의미하게 될 것이기 때문이다. 이것은 영업과 경영과 공학적 이해를 전체적으로 결합할 필요가 있다.
  • 자신의 필요와 그 소프트웨어가 설계된 목적이 얼마나 잘 맞는가?
  • 구입한 것 중 얼마만큼이 필요할 것인가?
  • 통합의 가치를 검토하는 데 드는 비용은 얼마나 되는가?
  • 통합하는 데 드는 비용은 얼마나 되는가?
  • 구입하게 되면 장기적 유지 보수 비용은 증가할 것인가, 감소할 것인가?
  • 그것을 구입함으로써 사업상 원치 않는 처지에 있게 되지는 않을 것인가? 

다른 사업 전체의 기반이 될 만큼 큰 과제를 직접 개발하는 일은 다시 한 번 생각해 봐야 한다. 그런 생각은 대개 팀에 공헌을 많이 하게 될 똑똑하고 낙관적인 사람들이 제안하기 마련이다. 그들의 생각을 꼭 채택하고 싶다면, 사업 계획을 수정할 각오도 해야 할 것이다. 그러나 뚜렷한 생각 없이 원래 프로젝트보다 더 큰 솔루션(solution) 개발에 투자하는 일은 하지 말라.

이 질문들을 고려한 후에 개발에 대한 것과 구입에 대한 것, 이렇게 두 가지 프로젝트 시안을 준비해야 할 것이다. 통합 비용도 반드시 고려해야 할 것이다. 두 솔루션 모두에 대해 장기적 유지 보수 비용도 고려해야 한다. 통합 비용을 추정하기 위해서는 그 소프트웨어를 구입하기 전에 철저하게 평가해야 할 것이다. 그것을 평가할 수 없다면, 그것을 구입함으로써 생기는 예상 밖의 위험 부담까지 가정하여 그 제품을 구입하는 것에 대해 결정해야 한다. 고려할 구입 결정 대상이 여럿이라면, 각각을 평가하기 위해 상당한 노력을 들여야 할 것이다.

3.3.5 전문가로 성장하는 방법 

자신의 권위보다 책임을 더 중하게 생각하라. 바라는 역할에 최선을 다하라. 자신을 개인적으로 도와준 사람들은 물론 조직 전체의 성공에 기여한 사람들에게 감사하라.

팀장이 되고 싶다면, 팀의 합의를 이루기 위해 애쓰라. 관리책임자가 되고 싶다면, 작업 일정에 책임을 지라. 팀장이나 관리책임자 대신 이 일을 맡는다면, 그들이 자유롭게 더 큰 일에 전념할 수 있을 것이므로, 그 일을 편하게 해 낼 수 있을 것이다. 그 일이 시험 삼아 해 보기에 너무 크다면, 한 번에 조금씩 하도록 하라.

자기 자신을 평가하라. 더 나은 프로그래머가 되고 싶다면, 존경하는 사람에게 어떻게 하면 그들과 같이 될 수 있는지 물어보라. 상사에게 물어볼 수도 있다. 그가 아는 것은 적어도 나의 경력에는 큰 영향을 미칠 것이다.

자기 일에 활용할 수 있는 새로운 기능을 배울 방법을 계획하라. 이것은, 새로운 소프트웨어 시스템에 대해 배우는 것과 같이 사소한 기술적 기능일 수도 있고, 글을 잘 쓰는 것과 같이 어려운 사회적 기능일 수도 있다.

3.3.6 면접 대상자를 평가하는 방법 

사원이 될 사람들을 평가하는 일은, 그 가치에 비해 큰 노력을 들이지 않고 있다. 잘못된 채용은, 잘못된 결혼과 마찬가지로, 끔찍한 일이다. 모든 사람들은 자기 에너지의 상당 부분을 인재 발굴에 바쳐야 하지만, 실제로 그런 경우는 드물다.

다양한 면접 유형이 있다. 어떤 것은 고문과 같아서, 지원자가 심한 스트레스를 받게 되어 있다. 이것은 스트레스를 받는 상황에서 인격적인 결점과 약점이 드러날 수 있게 하는 매우 중요한 목적이 있다. 지원자들은 자기 자신에 대해 정직한 만큼 면접관에게 정직할 것이다. 그런데 인간의 자기기만(self-deception) 능력은 대단하다.

최소한 두 시간 동안은 지원자에게 기술적 기능에 대해 묻는 구두시험을 실시해야 한다. 여러 번 하다 보면, 그들이 아는 것이 무엇인지 즉시 파악하고, 경계를 명확히 하기 위해 그들이 모르는 것에 대해 즉시 반응할 수 있게 될 것이다. 면접 대상자들은 이것을 순순히 받아들일 것이다. 나는 면접 대상자들에게서 회사를 선택하는 동기 중 하나가 면접의 질적 수준이라는 말을 몇 번 들은 적이 있다. 좋은 사람들은, 전에 일하던 곳이 어디인지, 어느 학교를 나왔는지, 그 밖의 다른 사소한 특성들보다는 자기 실력 때문에 채용되기를 원한다.

면접을 하면서 그들의 학습 능력에 대해서도 평가해야 한다. 이것은 그들이 현재 알고 있는 것이 무엇인가보다 더욱 더 중요하다. 까다로운 사람이라는 낌새도 알아차려야 한다. 면접 후에 기록해 둔 것들을 비교하면서 이런 것을 알아차릴 수 있을 것이다. 하지만 한참 면접이 진행되는 동안에 그것을 알아차리기는 어렵다. 다른 사람과 의사소통하고 같이 일하는 것을 얼마나 잘 하는가는 최신 프로그래밍 언어에 능통한 것보다 중요하다.

한 독자는 면접 대상자들에게 집에서 풀어오는 시험을 실시하여 결과가 좋았다고 한다. 이 방법은 면접 때 말은 잘 하지만 정작 코딩은 못 하는 사람들을 추려낼 수 있다는 장점이 있다. (사실 그런 사람들이 많다.) 개인적으로 이 기법을 써 보지는 않았지만, 괜찮아 보인다.

끝으로, 면접은 판매의 과정이기도 하다. 지원자들에게 회사나 프로젝트를 잘 팔아야 한다. 하지만, 프로그래머에게 이야기하는 것이므로, 진실을 윤색하려고 하지는 말라. 나쁜 점에서 시작하여 좋은 점에 대해 강한 인상을 주면서 마무리 하라.

3.3.7 화려한 전산 과학을 적용할 때를 아는 방법 

많은 프로그래머들이 알기는 하지만 거의 사용하지 않는 알고리듬, 자료 구조, 수학, 그 밖의 거창한 내용에 대한 지식들이 있다. 실제로 이런 훌륭한 지식들은 너무 복잡하여 일반적으로는 필요가 없다. 예를 들어, 대부분의 시간을 비효율적인 데이터베이스 질의를 만들고 있으면서 알고리듬을 개선한다는 것은 아무 의미가 없다. 프로그래밍을 하면서 시스템들이 서로 통신하게 한다거나 멋있는 사용자 환경(user interface)을 만들기 위해 매우 단순한 자료 구조를 사용해야 한다면 불행이 시작되는 것이다.

고급 기술을 사용하는 것이 적절한 때는 언제인가? 흔하지 않은 다른 알고리듬을 찾기 위해 책을 펼쳐야 하는 때는 언제인가? 그런 것을 사용하는 것이 유용할 경우가 있지만, 그 전에 주의해서 평가해야 한다.

사용하게 될지 모르는 전산 과학 기법에 대해 고려해야 하는 매우 중요한 세 가지 측면이 있다.
  • 캡슐화가 잘 되어 있어서, 다른 시스템에 미칠 위험 부담이 적고, 복잡도나 유지 보수 비용의 증가가 적은가?
  • 그 이득이 대단한가? (예를 들어, 기존의 것과 유사한 시스템이라면 두 배, 새로운 시스템이라면 열 배 정도의 이득)
  • 그것을 효과적으로 검사하고 평가할 수 있을 것인가? 

다소 화려해도 주변과 격리가 잘 되어 있는 알고리듬은 전체 시스템에 대해 두 배 정도로 하드웨어 비용을 줄이거나 그 만큼 성능을 향상시킬 수 있을 것이므로 그것을 고려하지 않는 것은 어리석은 일이 될 것이다. 이런 해결 방법을 주장하기 위한 한 가지 열쇠는, 그 기술에 대한 연구는 이미 잘 되어 있을 것이고 남은 문제는 통합의 위험 부담일 것이므로, 그 위험 부담이 실제로 매우 적다는 것을 보여 주는 것이다. 이렇게 될 때 프로그래머의 경험과 판단 능력이 그 화려한 기술과 어울려 진정한 상승효과를 낼 수 있을 것이며 통합도 쉽게 이뤄질 것이다.

3.3.8 비기술자들과 이야기하는 방법 

대중문화에서 기술자, 특히 프로그래머는 일반적으로 보통 사람들과는 다른 사람이라고 인식된다. 이것은 보통 사람들이 우리와 다르다는 뜻이다. 비기술자들과 대화할 때 이 사실을 염두에 두고 있는 것이 중요하다. 항상 듣는 사람을 이해해야 한다.

비기술자들은 똑똑하더라도 우리처럼 기술적인 것들을 만드는 일에 기초가 있지는 않다. 우리는 무엇인가를 만든다. 그들은 무엇인가를 팔거나 다루거나 세거나 관리하지만, 만드는 일에는 전문가가 아니다. 그들은 기술자들처럼 (물론 예외는 있지만) 팀으로 같이 일하는 것에도 익숙하지 않다. 주: 많은 독자들이 이 절의 내용이 오만하거나 자기 경험과는 거리가 멀다고 느낄 수 있다. 나는 비기술자도 매우 존중한다. 겸손한 척하려고 하는 말이 아니다. 나의 이런 신념들로 기분이 상했다면 용서를 구한다. 하지만 솔직하게 말해서 반대 사례를 경험하게 될 때까지는 그 신념을 거둘 수 없을 것 같다. 내가 이례적으로 운이 좋아서 그 동안 좋은 프로그래머들과 같이 일해 왔을 수도 있고, 다른 사람들이 프로그래머는 대화하기 부담스럽다는 고정관념(stereotype)을 일반적인 표준으로 생각하는 것일 수도 있다. 그들의 사회적 기능은 일반적으로, 팀이 아닌 환경에서 일하는 기술자들과 같거나 더 낫지만, 그들이 하는 일은 우리처럼 깊고 정확하게 의사소통을 해야 하거나 세부 과제를 조심스럽게 나눠야 하는 등의 일이 항상 요구되지는 않는다.

비기술자들은 간절히 다른 사람을 만족시키고 싶어 할 수도 있고, 기술자들에게 위협을 느낄 수도 있다. 우리와 똑같이, 그들도 기술자들을 만족시키기 위해서, 혹은 기술자들에게 다소 겁을 먹어서, 별 뜻 없이 '예'라고 말해 놓고는 나중에는 그 말에 책임지지 않을 수 있다.

비프로그래머들이 기술적인 것들을 이해할 수는 있지만 우리에게도 어려운 그것, 즉 기술적 판단 능력은 없다. 그들은 기술이 어떻게 적용되는지는 이해하지만, 왜 어떤 접근 방식은 석 달이나 걸리고, 다른 방식은 사흘이면 되는지 이해하지 못한다. (어쨌든, 프로그래머들이 이런 종류의 추정에 너무 냉정하다는 것도 맞는 말이다.) 이것은 그들과 함께 상승효과를 낼 수 있는 기회가 있다는 뜻도 된다.

팀에서 이야기할 때에는, 별 생각 없이, 일종의 줄임말을 사용할 것이다. 일반 기술이나 특히 함께 작업하는 제품에 대해 많은 경험을 공유하고 있기 때문에 그것이 효과적이다. 그런 경험의 공유가 없는 사람들에게 이야기할 때, 특별히 자기 팀원들도 같이 있다면, 줄임말을 사용하지 않는 데에 노력을 좀 들여야 한다. 이런 어휘는 우리와 그것을 공유하지 않는 사람들 사이에 벽을 만들고, 더 나쁘게는, 그들의 시간을 낭비하게 한다.

팀원들과 있을 때는 기본 가정이나 목표를 수시로 다시 말할 필요는 없으며, 대부분의 대화가 세부적인 것들에 초점이 맞춰진다. 외부인들과 함께 있을 때는 다른 방식으로 해야 한다. 그들은 우리가 당연하게 여기는 것을 이해하지 못할 수 있다. 어떤 것을 당연하게 여기고 다시 설명하지 않기 때문에, 실제로는 커다란 오해가 있는데도 서로를 잘 이해했다고 생각하면서 외부인들과 대화를 마칠 수도 있다. 자기 생각을 잘못 전달할 수 있다는 것을 항상 가정하고 실제로 그런 일이 없는지 잘 살펴봐야 한다. 그들이 잘 이해했는지 알아보기 위해, 요약을 하게 하거나 다른 말로 표현하게 해 보라. 그들을 자주 만날 기회가 있다면, 내가 효과적으로 대화하고 있는지, 어떻게 하면 더 잘 할 수 있을지 잠깐 물어보는 것도 좋다. 의사소통에 문제가 있다면, 그들에게 실망하기 전에 자신의 습관을 고칠 방법을 찾으라.

나는 비기술자들과 같이 일하는 것을 좋아한다. 가르치고 배울 기회가 많기 때문이다. 명확한 용어로 대화하면서, 예를 들어 가며 안내할 수도 있을 것이다. 기술자들은 무질서에서 질서를, 혼동됨에서 명확함을 찾아내도록 훈련받으며, 비기술자들은 우리의 이런 점을 좋아한다. 우리는 기술적 판단 능력이 있고 사업상의 문제들도 대개 이해할 수 있기 때문에, 종종 문제에 대한 간단명료한 해결책을 찾아내기도 한다.

비기술자들은 좋은 뜻으로, 그리고 잘 해 보려는 마음으로 우리가 일을 더 쉽게 해 낼 수 있을 것이라고 생각하는 해결책들을 제안하기도 한다. 사실은 훨씬 더 좋은 종합적 해결책이 존재하는데, 그것은 외부인들의 관점과 우리의 기술적 판단력이 함께 상승효과를 낼 때에만 보인다. 나는 개인적으로 익스트림 프로그래밍(Extreme Programming)을 좋아한다. 그것이 이런 비효율성을 중점적으로 다루고 있으며, 아이디어와 그에 대한 비용 추정을 신속하게 짝지음으로써, 비용과 이득이 최상으로 결합되는 아이디어를 쉽게 찾을 수 있게 해 주기 때문이다.

4 상급자 

4.1 기술적 판단 능력 

4.1.1 어려운 것과 불가능한 것을 구분하는 방법 

어려운 일은 해 내고, 불가능한 일은 골라내는 것이 우리가 할 일이다. 대부분의 현직 프로그래머들의 관점에서 보면, 단순한 시스템에서 나올 수 없거나 비용을 추정할 수 없는 일은 불가능한 것이다. 이 정의에 따르면 연구라고 불리는 것은 모두 불가능한 일이다. 일거리들의 많은 부분이 어렵기는 하지만, 반드시 불가능한 것은 아니다.

이 구분은 전혀 우스운 것이 아니다. 과학적 관점에서든 소프트웨어 공학적 관점에서든, 실제로 불가능한 일을 하라는 요구를 받는 경우가 많을 것이기 때문이다. 어렵기는 해도 사업주가 원하는 것을 최대한 끌어낼 수 있는 합리적인 해결책을 찾도록 돕는 것이 우리가 할 일이다. 자신 있게 일정을 잡을 수 있고 위험 부담을 잘 이해하고 있다면, 그 해결책은 어려운 것일 뿐이다.

예를 들어, '각 사람에게 가장 매력적인 머리 모양과 색깔을 계산할 수 있는 시스템을 개발하라'와 같은 막연한 요구 사항을 만족시키는 것은 불가능한 일이다. 요구 사항이 좀 더 뚜렷해질 수 있다면, 그 일이 어려울 뿐인 일로 바뀌기도 한다. 예를 들어 다음과 같은 식이다. '어떤 사람에게 매력적인 머리 모양과 색깔을 계산할 수 있는 시스템을 개발하되, 그들이 그것을 미리 보고 수정할 수 있게 하여, 처음 제안한 스타일에 대한 고객 만족을 극대화함으로써 많은 수입을 얻을 수 있게 하라.' 성공에 대한 뚜렷한 정의가 없다면 성공할 수 없을 것이다.

4.1.2 내장 언어를 활용하는 방법 

시스템에 프로그래밍 언어를 내장하는(embedding) 것은 프로그래머에게는 에로틱하다고 할 만한 황홀함을 느끼게 한다. 이것은 해 볼 수 있는 가장 창의적인 활동들 중 하나이다. 이것은 시스템을 굉장히 강력하게 만들어 준다. 이것을 통해 자신의 창의적이고 프로메테우스적인 능력을 최대한 발휘할 수 있다. 이것은 시스템을 친구로 만들어 준다.

세계적으로 가장 우수한 텍스트 편집기들은 모두 내장 언어를 갖추고 있다. 사용자가 그 언어에 완전히 통달하는 경지에까지 이를 수도 있다. 물론, 그것이 텍스트 편집기 안에 들어 있으므로, 써 보고 싶은 사람들은 쓸 수 있고 그렇지 않은 사람들은 그럴 필요 없도록, 그 언어의 사용을 선택 사항으로 둘 수도 있다.

나를 비롯하여 다른 많은 프로그래머들이 특수한 목적의 내장 언어를 만들고 싶다는 유혹에 빠지곤 한다. 나는 두 번 그런 적이 있다. 내장 언어로 특별히 설계된 언어들이 이미 많이 나와 있다. 새로운 것을 또 만들기 전에 한 번 더 생각해 볼 필요가 있다.

언어를 내장하기 전에 스스로 물어봐야 하는 진짜 질문은 이것이다. 이것이 사용자의 문화와 잘 맞을 것인가, 그렇지 않을 것인가? 사용자가 모두 비프로그래머라면 그것이 무슨 도움이 될 것인가? 사용자가 모두 프로그래머라면 오히려 API를 선호하지 않을 것인가? 무슨 언어로 할 것인가? 프로그래머들은 사용 범위가 좁은 새 언어는 배우고 싶어 하지 않는다. 하지만 그것이 그들의 문화와 잘 맞물린다면 많은 시간을 들이지 않고서도 배울 수 있을 것이다. 새로운 언어를 만든다는 것은 즐거운 일이다. 하지만 그렇다고 해서 사용자들의 필요에 대해 눈이 가려져서는 안 된다. 정말로 근본적인 필요와 아이디어가 있는 것이 아니라면, 사용자들이 이미 친숙한 기존의 언어를 사용해서 부담을 줄여 주는 것이 어떤가?

4.1.3 언어의 선택 

자신의 일을 사랑하는 고독한 프로그래머(즉, 해커)는 과제에 가장 잘 맞는 언어를 선택할 수 있다. 대부분의 현직 프로그래머들은 자기가 사용할 언어를 마음대로 고를 수 있는 경우가 드물다. 일반적으로, 이 문제는 잘난 체하는(pointy-haired) 상사들이 마음대로 결정한다. 이들은 기술적으로 결정하기보다는 정략적으로 결정하고, 아직 일반화되지 않은 어떤 도구가 가장 좋다는 것을 (대개 실무 경험에 의해) 알면서도 재래식이 아닌 도구를 사용하자고 나설 만한 용기는 없다. 어떤 경우에는 팀 전체, 더 넓게는 공동체 전체의 통일이 매우 실제적인 이득이 있기 때문에 개인적인 입장에서 선택하는 것을 배제하기도 한다. 관리책임자들은 정해진 언어에 대한 경험이 있는 프로그래머들을 채용해야 하는 필요에 따라 움직이기도 한다. 그들이 프로젝트나 회사에 가장 큰 이익이 된다고 생각하는 것을 위해 일한다는 것은 분명하며, 그것에 대해 존중받을 만하다. 하지만 나는 개인적으로 이것이 흔히 마주치게 되는 가장 낭비적이고 잘못된 일이라고 생각한다.

물론, 모든 일이 1차원적인 경우는 없다. 한 가지 중심 언어가 필수로 정해지고 그것을 내가 어떻게 할 수 없다 해도, 도구나 다른 프로그램을 다른 언어로 작성할 수 있거나 그렇게 해야 하는 경우가 종종 있다. 언어를 내장해야 한다면 (이것은 항상 생각해야 한다!) 언어를 선택할 때 사용자들의 문화를 많이 고려해야 할 것이다. 회사나 프로젝트에 기여하기 위해 그 일에 가장 적합한 언어를 사용하는 것의 장점을 잘 활용해야 하며, 이것을 통해 일이 더욱 흥미로워질 것이다.

프로그래밍 언어는, 그것을 배우는 것이 자연 언어를 배우는 것만큼 어려운 일이 전혀 아니라는 점에서, 표기법들(notations)이라고 부르는 것이 실제에 가깝다. 초보자들이나 외부인들에게는 "새로운 언어 배우기"가 멈칫하게 될 과제로 보인다. 하지만 세 가지 정도 언어를 체험해 보면, 그 일은 주어진 라이브러리들에 익숙해지는 문제일 뿐이다. 구성요소들이 서너 가지 언어로 되어 있는 큰 시스템이 있을 때 그것을 지저분하게 뒤범벅이 되어 있다고 생각할 수 있지만, 나는 그런 시스템이 한 가지 언어만으로 되어 있는 시스템보다 여러 면에서 더 튼튼한 경우가 많다고 하겠다.
  • 서로 다른 표기법으로 작성된 구성요소들은 필연적으로 결합도(coupling)가 낮아진다. (깔끔한 인터페이스는 없겠지만)
  • 각 요소들을 개별적으로 재작성함으로써 새로운 언어/플랫폼으로 발전시키기 쉽다.
  • 실제로는 어떤 모듈들이 최신의 것으로 갱신되었기 때문일 수도 있다. 

심리적인 효과일 뿐인 것도 있지만, 심리적인 것도 중요하다. 결국 언어에 대한 독재는 그것이 주는 유익보다 거기에 드는 비용이 더 크다.

4.2 현명하게 타협하기 

4.2.1 작업 일정의 압박과 싸우는 방법 

출시 시간(time-to-market)의 압박은 좋은 제품을 신속하게 내놓기 위한 압박이다. 이것은 재정적 현실을 반영하는 것이기 때문에 나쁠 것도 없고, 어떤 점에서는 건전한 것이다. 작업 일정의 압박은 내놓을 수 있는 시간보다 더 빨리 내놓기 위한 압박이며, 이것은 낭비적이고 건전하지도 않지만, 너무도 흔하다.

작업 일정의 압박은 몇 가지 이유로 존재한다. 프로그래머들에게 과제를 맡기는 사람들은 우리가 얼마나 강한 직업윤리를 갖고 있으며 프로그래머가 된다는 것이 얼마나 재미있는 일인지 충분히 인식하지 못한다. 아마도 그들은 자신의 행동 방식을 우리에게 그대로 비춰 보기 때문에, 더 빨리 하라고 요구하면 더 열심히 일하게 될 것이라고 믿는다. 이것은 어쩌면 실제로 사실일 수도 있지만, 그 효과는 매우 작으며 손해는 매우 크다. 게다가 그들은 소프트웨어를 만들기 위해 실제로 무엇이 필요한지 볼 수 있는 눈이 없다. 볼 수도 없고 스스로 만들 수도 없기 때문에, 그들이 할 수 있는 단 한 가지는 출시 시간의 압박을 보면서 프로그래머들에게 그것에 대해 떠들어대는 일이다.

작업 일정의 압박과 싸우는 열쇠는 그것을 출시 시간의 압박으로 바꿔 놓는 것이다. 이렇게 하는 방법은 가용 인력과 제품 사이의 관계를 잘 볼 수 있게 하는 것이다. 개입된 모든 인력에 대해 정직하고 상세하고 무엇보다도 이해할 만한 추정치를 내놓는 것이 이것을 위한 가장 좋은 방법이다. 이것은 직무의 조정 가능성에 대한 관리상의 의사결정을 잘 할 수 있게 해 준다는 추가적인 장점이 있다.

이런 추정을 통해 명백해지는 중요한 통찰은, 인력이 비압축성 유체(incompressible fluid)와 같다는 것이다. 그릇의 부피보다 더 많이 물을 눌러넣을 수 없듯이, 일정 시간 안에 더 많은 것을 우겨넣을 수 없다. 어떤 점에서 프로그래머는 '못 합니다.'라고 하기보다는, '원하는 그 일을 위해 무엇을 포기하겠습니까?'라고 해야 할 것이다. 추정을 명확하게 함으로써 프로그래머가 더욱 존중받게 되는 효과가 있을 것이다. 다른 직종의 전문가들은 바로 이렇게 행동한다. 이로써 프로그래머들의 고된 일이 눈에 보이게 될 것이다. 비현실적인 작업 일정을 잡았다는 사실도 고통스럽겠지만 모든 사람에게 분명히 드러날 것이다. 프로그래머들은 함부로 현혹할 수 있는 사람들이 아니다. 그들에게 비현실적인 것을 요구하는 일은 예의 없고 비도덕적인 일이다. 익스트림 프로그래밍(Extreme Programming)은 이것을 상세히 설명하고 있으며 그 과정을 확립해 놓고 있다. 나는 모든 독자들이 이 기법을 활용할 수 있을 만큼 운이 좋기를 바란다.

4.2.2 사용자를 이해하는 방법 

우리에게는 사용자를 이해하고, 또한 상사가 그 사용자를 이해할 수 있게 도와 줄 의무가 있다. 사용자는 우리처럼 제품 생산에 깊이 개입되어 있지 않기 때문에 다음과 같이 조금 특이하게 행동한다.
  • 사용자는 일반적으로 짧게 말하고 끝낸다.
  • 사용자는 자기 일이 따로 있다. 그들은 대개 제품이 크게도 아니고 약간 개선되었으면 좋겠다고 생각한다.
  • 사용자는 그 제품 사용자들 전체를 볼 수 있는 눈이 없다. 

우리는 그들이 원한다고 말한 것이 아니라, 정말로 그들이 원하는 것을 제공할 의무가 있다. 그래서 일에 착수하기 전에 이쪽에서 먼저 제안을 하고, 그것이 정말로 그들이 원하는 것이라는 동의를 얻는 것이 더 나을 수 있다. 하지만 그들은 이렇게 하는 것이 바람직하다는 것을 모를 수도 있다. 그것을 위한 자신의 아이디어에 대한 자신감의 정도는 경우에 따라 달라져야 한다. 고객이 정말로 무엇을 원하는지 아는 것에 대해, 자만심과 거짓 겸손, 둘 다 경계해야 한다. 프로그래머들은 설계하고 만들어내도록 훈련된다. 시장 연구자들은 사람들이 무엇을 원하는지 알아내도록 훈련된다. 이 두 종류의 사람들, 아니 한 사람 안에 있는 두 가지 사고방식은 함께 조화를 이룰 때 정확하게 통찰할 수 있는 최상의 조건이 된다.

사용자들과 시간을 많이 보낼수록 무엇이 실제로 성공적일지 더 잘 이해하게 될 수 있을 것이다. 가능한 한 많이 자신의 생각을 사용자들의 생각과 비교하여 검사해 봐야 한다. 할 수 있다면 그들과 함께 먹고 마시기도 해 봐야 한다.

가이 카와사키(Guy Kawasaki)는 사용자들의 말을 듣는 것에 더하여 그들이 무엇을 하는지 관찰하는 것의 중요성을 강조한 바 있다. <Rules>

내가 알기로, 의뢰인들이 진정으로 원하는 것이 무엇인지 그들 자신의 마음에 분명해지게 하는 일에 계약직 프로그래머나 컨설턴트들이 엄청난 어려움을 겪는 경우가 종종 있다. 컨설턴트가 될 생각이 있는 사람은 의뢰인을 선택할 때 그들의 수표책뿐만 아니라 그들의 머리 속이 얼마나 명료한지도 확인하라고 권하고 싶다.

4.2.3 진급하는 방법 

어떤 역할로 진급하고 싶다면, 그 역할을 먼저 실행하라.

어떤 직위로 진급하고 싶다면, 그 직위에 기대되는 것이 무엇인지 파악하여 그것을 행하라.

임금 인상을 원한다면, 정확한 정보로 무장하고 협상하라.

진급을 할 때가 지났다고 느껴지면, 상사에게 그것에 대해 이야기하라. 진급을 하기 위해 무엇을 해야 하는지 숨기지 말고 그들에게 질문하라. 진부한 이야기로 들리겠지만, 스스로 무엇이 필요하다고 인식하는 것과 상사가 인식하는 것이 상당히 다른 경우가 종종 있다. 또한 이것은 어떤 식으로든 상사에게 그 일을 확실히 못 박아 두는 것도 된다.

대부분의 프로그래머들이 자신의 상대적 능력에 대해 어떤 면에서는 과장되게 생각하는 것 같다. 하지만, 우리가 모두 상위 10%가 될 수는 없는 노릇이다! 그러나, 심각하게 진가를 인정받지 못하는 사람들도 많이 봐 왔다. 모든 사람의 평가가 항상 정확하게 실체와 일치할 것이라고 기대할 수는 없지만, 한 가지 단서가 있다면 사람들은 일반적으로 적당히 공정할 것이라고 생각한다. 자신의 일을 드러내 보여주지 않는다면 제대로 평가받을 수도 없다. 때로는 우연한 실수나 개인적인 버릇 때문에, 충분히 주목받지 못하기도 한다. 집에서 주로 일하거나 팀이나 상사와 지리적으로 떨어져 있는 것 때문에 이것이 특히 어려워지기도 한다.

4.3 팀을 위해 일하기 

4.3.1 재능을 개발하는 방법 

니체(Nietzsche)는 이렇게 과시하며 말했다. <Stronger>

나를 파괴하지 않는 것은 나를 강하게 하는 것이다. 


우리가 가장 많이 책임져야 할 대상은 우리 팀이다. 팀원들을 모두 잘 알아야 한다. 팀에게 도전적으로 요구하더라도, 지나치게 무거운 짐을 지워서는 안 된다. 그들이 긴장을 유지하는 방법에 대해 그들과 이야기해 봐야 할 것이다. 그들이 그것을 기꺼이 받아들인다면(buy in), 더욱 동기가 높아질 것이다. 모든 프로젝트, 또는 하나 건너 하나의 프로젝트마다 그들이 제안한 방법과 그들에게 좋을 것 같다고 생각되는 방법으로 긴장을 유지하게 해 줘야 한다. 그들에게 일을 더 많이 맡기는 것보다는, 새로운 기능을 알려주거나 더 좋게는 팀에서 능력을 발휘할 새로운 역할을 부여하여 긴장을 유지하게 하라.

다른 사람들은 물론 자기 자신도 간혹 일이 안 풀릴 수 있다는 것을 인정해야 하며, 일정대로 일이 진행되지 않을 경우를 대비한 계획을 세워 놓아야 한다. 항상 일이 잘 된다면, 모험은 아무 의미가 없을 것이다. 일이 안 풀리는 경우가 없다는 것은, 위험 부담 없이 편하게만 일을 하고 있다는 뜻이다. 누군가 일이 잘 안 됐다면, 그들이 성공한 것처럼 대우할 필요는 없겠지만, 최대한 부드럽게 대해야 한다.

모든 팀원들이 기꺼이 받아들이고 동기를 높일 수 있도록 노력하라. 팀원 각자에게 그들이 동기가 높지 않을 때 어떻게 해 주면 좋은지 터놓고 물어 보라. 그들을 불만족스러운 채로 내버려둬야 할 경우도 있지만, 각자가 바라는 것이 무엇인지는 알고 있어야 한다.

낮은 의욕이나 불만족 때문에 자기 몫의 짐을 일부러 지지 않는 사람을 무시해 버리거나 되는 대로 내버려 둘 수는 없다. 그들의 동기와 생산성을 높이도록 노력해야 한다. 참을 수 있는 한 계속 노력하라. 인내의 한계를 넘어섰다면 그들을 해고하라. 일부러 자기 능력 이하로 일하는 사람들을 팀에 계속 있게 할 수는 없다. 그렇게 하는 것은 팀에 공정한 일이 아니다.

능력 있는 팀원들에게는 그들이 능력 있다고 생각한다는 사실을 공개적으로 이야기함으로써 그 사실을 확인시켜 주라. 칭찬은 공개적으로, 비판은 사적으로 해야 한다.

능력 있는 팀원들은 자연적으로 능력이 모자라는 팀원들보다 더 어려운 과제를 맡는다. 이것은 아주 자연스러운 일이며, 모두가 다 열심히 일하는 한 아무도 이것 때문에 귀찮아하지 않을 것이다.

좋은 프로그래머 한 사람이 안 좋은 프로그래머 열 사람보다 생산성이 높은데도 그것이 봉급에 반영되지 않는 것은 이상한 일이다. 이것 때문에 미묘한 상황이 생긴다. 능력이 모자란 프로그래머가 길을 비켜 주면 일을 더 빨리 진행할 수 있는 경우가 사실 종종 있다. 그렇게 하면 실제로 단기간에는 더 많은 성과를 낼 수도 있다. 하지만, 부족(tribe) 전체로서는 여러 중요한 이득을 잃게 된다. 능력이 모자라는 팀원의 훈련, 팀 내 지식의 확산, 능력 있는 팀원이 없을 때 대신할 능력 등이 그것이다. 능력 있는 사람들은 이 점에 관해서는 너그러울 필요가 있고 그 문제를 다각도로 고려해야 한다.

능력 있는 팀원에게는 도전할 만하면서 범위가 잘 정의된 과제를 맡겨 보는 것도 좋다.

4.3.2 일할 과제를 선택하는 방법 

프로젝트의 어느 부분을 맡아 할 것인지 선택할 때에는 자신의 개인적 필요와 팀 전체의 필요 사이에서 균형을 잡아야 한다. 물론 가장 잘 하는 일을 선택해야겠지만, 일을 더 많이 하는 것보다는 새로운 기능을 발휘할 수 있는 것을 통해 스스로 긴장을 유지할 수 있는 길을 찾아보도록 하라. 지도력과 의사소통 기능은 기술적 기능보다 더 중요하다. 자신의 능력이 뛰어나다면, 더 어렵고 위험부담이 큰 과제를 맡고, 위험부담을 줄이기 위해 프로젝트에서 그 일을 최대한 빨리 처리하라.

4.3.3 팀 동료들이 최대한 능력을 발휘하게 하는 방법 

팀 동료들이 최대한 능력을 발휘하게 하기 위해서는, 단체정신을 키우고 팀원 모두가 개별적으로 도전을 느끼고 개별적으로 일에 몰두하도록 격려하라.

단체정신을 키우기 위해서는, 로고가 새겨진 옷이나 파티처럼 다소 진부한 것도 좋지만, 개인적으로 존중하는 것만큼 좋지는 않다. 모두가 모두를 존중하면 아무도 다른 사람을 깎아내리고 싶지 않을 것이다. 단체정신은 사람들이 팀을 위해 희생하고 자신의 이익보다 팀의 이익을 먼저 생각할 때 만들어진다. 팀장으로서 이 점에 대해 솔선해서 한 것 이상으로 다른 사람에게 요구할 수는 없을 것이다.

팀에 대한 지도력의 한 가지 열쇠는 모든 사람이 기꺼이 받아들일 수 있는 합의를 이뤄내는 것이다. 이것은 팀 동료들이 잘못된 일을 하는 것을 인정한다는 말이기도 하다. 즉, 그것이 프로젝트에 너무 큰 피해를 주지만 않는다면, 비록 나는 그렇게 하는 것이 잘못된 것이라는 분명한 확신이 있다 해도, 팀원 중 누가 자기 방식대로 일하는 것을, 합의에 따라 그렇게 하게 두어야 한다. 이런 일이 생길 때는, 찬성하지 말고 공개적으로 반대하되, 합의된 것은 받아들이라. 마음이 상했다거나 어쩔 수 없이 그렇게 한다는 식으로 말하지 말고, 그것에 반대하지만 팀의 합의를 더 중요하게 생각한다고 꾸밈없이 말하라. 이렇게 할 때 그들이 돌이키기도 한다. 그들이 돌이켰다면 그들에게 원래 계획대로 밀고 나가라고 고집하지는 말라.

그 문제에 대해 적절한 모든 관점에서 논의한 후에도 동의하지 않는 사람이 있다면, 이제 결정을 내려야 하며 이것이 나의 결정이라고 꾸밈없이 단언하라. 자신의 결정이 잘못되었는지 판단할 방법이 있거나 나중에 잘못된 것으로 밝혀진다면 할 수 있는 대로 빨리 돌이키고 옳았던 사람을 인정하라.

단체정신을 키우고 팀을 효과적으로 만들기 위해 어떻게 하는 것이 좋을지 팀 전체와 팀원 각자에게 물어보라.

자주 칭찬하되 분별없이 하지는 말라. 특별히 나와 의견이 다른 사람도 칭찬할 만할 때에는 바로 칭찬하라. 공개적으로 칭찬하고 사적으로 비판하라. 한 가지 예외는 있다. 과실을 바로잡아 가치가 증대된 것(growth)을 공개적으로 칭찬할 때에는 원래의 과실이 눈길을 끄는 난처한 상황이 될 수도 있으므로, 가치 증대에 대해서는 사적으로 칭찬하는 것이 좋다.

4.3.4 문제를 나누는 방법 

소프트웨어 프로젝트를 맡아서 각 사람이 수행할 과제들로 나누는 것은 재미있는 일이다. 이것은 초반에 해야 한다. 관리책임자들이 그 일을 수행할 각 사람들에 대해 고려하지 않아도 시간을 추정할 수 있다고 생각하는 경우가 있는 것 같다. 이것은 불가능한 일이다. 각자의 생산성은 아주 크게 차이가 나기 때문이다. 어떤 구성요소(component)에 대한 특정 지식이 있는 사람도 계속 변화해 가므로, 수행 능력에서 여러 배의 차이가 날 수도 있다.

지휘자가 연주할 악기의 음색을 고려하고 운동 경기 코치가 각 선수의 능력을 고려하는 것처럼, 경험 많은 팀장이라면 프로젝트를 과제들로 나누는 일을, 그 과제가 할당될 팀원들과 분리해서 생각하지는 않을 것이다. 이것은 수행 능력이 뛰어난 팀이 해체되어서는 안 되는 이유이기도 하다.

여기에도 위험이 다소 따르는데, 사람들이 능력을 쌓아가는 과정에서 따분해져서 약점을 개선하거나 새로운 기능을 배우려고 하지 않는 경우가 그것이다. 하지만, 전문화는 남용되지만 않는다면 생산성 향상에 매우 유용한 도구가 된다.

4.3.5 따분한 과제를 다루는 방법 

간혹 회사나 프로젝트의 성공에 결정적이기 때문에 과제가 따분해도 피할 수 없는 경우가 있다. 이런 과제들은 그것을 해야 하는 사람들의 사기를 실제로 떨어뜨릴 수 있다. 이것을 다루는 가장 좋은 기법은 래리 월(Larry Wall)이 말한 '프로그래머의 게으름의 미덕(virtue of Laziness)'을 불러일으키고 북돋아 주는 것이다. 자신이나 동료 대신 컴퓨터가 그 과제를 처리하게 하는 방법이 없는지 찾아보라. 수작업으로 한 주에 할 과제를 해결할 프로그램을 짜는 데 한 주가 걸린다 해도 이것이 더욱 교육적이고 필요하면 반복해서 쓸 수도 있으므로 더 큰 이득이 있는 것이다.

모든 방법이 수포로 돌아간다면, 따분한 일을 해야 하는 사람들에게 양해를 구해야겠지만 어떤 경우에도 그들이 혼자 하게 내버려 두지는 말라. 최소한 두 사람으로 팀을 짜서 그 일을 맡기고 그 일을 완수하기 위해 성실히 협력할 수 있도록 격려하라.

4.3.6 프로젝트를 위한 지원을 얻는 방법 

프로젝트를 위한 지원을 얻기 위해서는, 조직 전체가 추구할 실제적인 가치를 잘 드러내는 이상(vision)을 만들고 알려야 한다. 이상을 만드는 일에 다른 사람들도 동참하도록 노력하라. 이것을 통해 그들에게는 우리를 지원할 이유가 생기며 우리에게는 그들의 아이디어를 얻는 혜택이 생긴다. 프로젝트를 위한 핵심적인 지원 인사들을 개별적으로 모집하라. 갈 수 있는 곳이면 어디든 가서, 말만 하지 말고 보여주라. 할 수 있다면, 자신의 아이디어를 시연할 수 있는 시제품(prototype)이나 실물모형(mockup)을 만들라. 시제품은 어느 분야에서든 효력이 있지만 소프트웨어 분야에서는 글로 쓴 어떤 설명보다 훨씬 더 우세하다.

4.3.7 시스템이 자라게 하는 방법 

나무의 씨앗에는 어른 나무의 밑그림(idea)이 들어있지만 아직 그 형태나 능력이 완전히 발현되지는 않았다. 싹이 자라고 커진다. 점점 어른 나무를 닮아 가며 점점 더 쓸모 있게 되어 간다. 마침내 열매를 맺고, 그 후에는 죽어서 다른 생물을 위한 거름이 된다.

과장된 표현일 수도 있지만 소프트웨어도 그렇게 취급된다. 다리(bridge)는 그렇지 않다. 미완성 다리는 있어도 '아기 다리'는 없다. 다리는 소프트웨어에 비해 아주 단순하다.

소프트웨어가 자란다고 생각하는 것이 좋다. 그렇게 생각하면 머리 속에 완벽한 그림이 그려지기 전에도 훌륭히 전진해 갈 수 있기 때문이다. 사용자들의 반응을 듣고 소프트웨어의 성장을 바로잡아 줄 수 있다. 약한 가지를 쳐 주는 것도 건강에 좋다.

프로그래머는 전달받아 사용할 수 있는 완성된 시스템을 설계해야 한다. 그런데 고급 프로그래머는 그 이상을 할 수 있어야 한다. 완성된 시스템으로 귀결되는 성장 경로를 설계할 수 있어야 한다. 아이디어의 싹을 가지고 가능한 한 평탄한 경로를 따라 유용한 완성품이 만들어질 수 있게 하는 것이 우리가 할 일이다.

이를 위해서는, 최종 결과를 시각화하고 그것에 대해 기술팀이 흥미를 가질 수 있도록 전달해야 한다. 또한 현재 그들의 위치에서 그들이 원하는 위치까지 가는 경로를 비약 없이 잘 전달해야 한다. 나무는 그 기간 내내 살아 있어야 한다. 어느 순간에 죽었다가 나중에 부활할 수는 없다.

이런 접근 방법은 나선형 개발(spiral development)에 그대로 반영되어 있다. 그 경로에 따라 진도를 표시하기 위해 간격이 너무 멀지 않은 진도표(milestones)를 사용한다. 사업이라는 무한 경쟁의 환경에서는, 비록 잘 설계된 최종 목표와는 거리가 멀다 해도 진도별 배포판(milestone release)을 계속 내면서 최대한 빨리 돈을 버는 것이 상책이다. 프로그래머의 임무 중 하나는, 일정표에 명시되는 성장 경로를 현명하게 선택함으로써 즉각적인 이득과 미래의 이득 사이에 균형을 잡는 일이다.

고급 프로그래머는 소프트웨어와 팀과 개개인의 성장에 대한 3중의 책임이 있다.

독자인 롭 하퍼닉(Rob Hafernik)이 이 절에 대해 다음의 의견을 보내 왔는데, 전문을 인용하는 것이 가장 좋을 것 같다.

여기에서는 그 중요성이 덜 강조된 것 같습니다. 이것은 시스템만의 문제가 아니며, 알고리듬, 사용자 환경(user interface), 데이터 모형(data model) 등의 문제이기도 합니다. 이것은 대형 시스템의 작업을 할 때 중간 목표들을 향해 진도를 맞춰 가기 위해서는 정말로 대단히 중요합니다. 끝까지 다 가서야 전체가 전혀 작동하지 않는다는 사실을 알게 되는 공포 상황만큼 나쁜 것은 없을 것입니다. (보우터 뉴스 서비스(Voter News Service)가 최근 해체된 것을 보십시오. 역자 주: 보우터 뉴스 서비스는 미국 언론사들이 출구 조사를 위해 공동 설립한 기관으로, 2002년 11월 미국 중간 선거를 대비하여 1,000만 달러 이상을 투자하여 시스템을 새롭게 갖추었으나, 선거 당일에 총체적인 문제가 생겨 출구 조사 결과 발표를 포기하고 말았다. 이 기관은 2003년 1월 해체되었다.) 한 걸음 더 나아가 이것은 자연 법칙이라고까지 말하고 싶습니다. 대형의 복잡한 시스템은 무에서 시작하여 구현할 수 없습니다. 의도한 단계를 거쳐 가면서 단순한 시스템에서 복잡한 시스템으로 진화하는 것만 가능합니다. 


이 인용글에 대해서는 "빛이 있으라(Fiat lux)!" 하고 응답할 수밖에 없을 것이다.

4.3.8 대화를 잘 하는 방법 

대화를 잘 하기 위해서는 우선 그것이 얼마나 어려운 것인지 인식해야 한다. 이것은 기능 자체에 대한 기능이다. 대화할 대상자들이 결점이 있는 사람들이라는 사실 때문에 이 일은 더욱 어려운 일이 된다. 그들은 나를 이해하는 일에 별로 노력을 들이지 않는다. 그들은 말도 잘 못 하고 글도 잘 못 쓴다. 그들은 대개 과로하고 있거나 따분해하고 있으며, 지금 말하고자 하는 큰 문제들보다는 자기 자신의 일에만 초점을 맞추고 있는 것 같다. 개설되어 있는 강좌를 통해 글쓰기, 연설, 듣기 기능을 연습하면, 이것들을 잘 하게 될 때 문제가 어디에 있는지, 그것을 어떻게 고칠 수 있는지 더 쉽게 볼 수 있다는 장점이 있다.

프로그래머는 자기 팀과 대화하는 일에 생존이 달려 있는 사회적 동물이다. 고급 프로그래머는 팀 밖의 사람들과 대화하는 일에 만족이 달려 있는 사회적 동물이다.

프로그래머는 무질서에서 질서를 끌어낸다. 이것을 하는 한 가지 흥미로운 방법은 팀 밖에서 어떤 제안을 시작하게 하는 것이다. 이것은 뼈대(strawman)나 백지 형식으로, 혹은 단지 구두로 시작될 수 있다. 이렇게 이끌어 가는 것은 토론의 조건을 설정한다는 점에서 굉장히 큰 장점이 있다. 이를 통해 나 자신이 비판, 더 나쁘게는 거부와 무시에 내놓인다. 고급 프로그래머는 고유한 권한과 그에 따른 고유한 책임이 있으므로, 이것을 받아들일 각오를 해야 한다. 프로그래머가 아닌 사업가들은 여러 가지 점에서 지도력을 발휘하기 위해 프로그래머가 필요하다. 프로그래머들은 현실에 기초하여 아이디어와 현실을 이어주는 다리의 한 부분이다.

나도 대화를 잘 하는 일에 정통하지는 않지만, 현재 노력하고 있는 것은 네 갈래의 접근 방식이다. 이것은, 아이디어를 정돈하고 충분히 준비를 갖춘 다음, 구두로 이야기를 하고, 사람들에게 백지를 (실제 종이로든, 전자적으로든) 나눠주고, 시연을 하고, 인내심을 가지고 이 과정을 반복하는 것이다. 이런 어려운 대화 과정에서 충분히 인내심을 갖지 않는 때가 많다고 생각한다. 자기 아이디어가 즉각 받아들여지지 않는다고 해서 낙담해서는 안 된다. 그것을 준비하는 데에 노력을 들였다면, 그것 때문에 나를 하찮게 생각하는 사람은 없을 것이다.

4.3.9 사람들에게 듣고 싶어 하지 않는 말을 하는 방법 

사람들에게 그들을 불편하게 할 말을 해야 할 때가 있다. 이 일은 어떤 이유가 있기 때문에 하는 것이라는 사실을 기억하라. 그 문제에 대해 아무 것도 할 수 없다 해도, 그들에게 할 수 있는 한 빨리 말해서 그들이 그 사실을 숙지하고 있게 해야 한다.

누군가에게 문제점에 대해 말하는 가장 좋은 방법은 해결책을 동시에 제시하는 것이다. 두 번째로 좋은 방법은 그 문제점에 대해 도움을 요청하는 것이다. 그 사람이 믿지 않을 위험이 있다면, 그 말을 지지해 줄 사람을 모아 봐야 할 것이다.

해야 하는 가장 불쾌하면서 일상적인 말들 중 하나는 ‘예정일을 넘길 것 같군요.’라고 말하는 것이다. 양심적인 프로그래머라면 이런 말을 하기가 싫을 테지만, 그래도 최대한 빨리 해야 한다. 진도 날짜를 지나쳤을 때, 할 수 있는 것이 모든 사람들에게 그 사실을 알리는 일밖에 없다 해도, 대응이 지연되는 것보다 안 좋은 일은 없다. 이런 일을 할 때에는, 물리적으로 같이 하지는 못하더라도, 정신적으로라도 팀으로 같이 하는 것이 더 좋다. 지금의 위치와 그것을 해결하기 위한 일에 대해 팀원들의 의견이 필요할 것이며, 팀원들도 그 결과를 함께 직시해야 할 것이다.

4.3.10 관리상의 신화들을 다루는 방법 

신화라는 단어는 허구를 뜻하기도 한다. 하지만 좀 더 깊은 함축이 있다. 이것은 우주에 대해, 그리고 인류와 우주의 관계에 대해 설명하는 종교적으로 중요한 이야기를 뜻하기도 한다. 관리책임자들은 프로그래머로서 배운 것은 잊어버리고, 어떤 신화들을 믿곤 한다. 그 신화들이 거짓이라고 그들에게 설득하려고 하는 것은, 독실한 종교인에게 그들의 믿음에 대한 환상을 깨뜨리려고 하는 것처럼 귀에 거슬리고 성공 가능성이 없는 일이다. 그렇기 때문에 이런 신념들을 신화라고 인식해야 한다.
  • 문서는 많이 만들수록 좋다. (그들은 문서를 원하지만, 그들은 문서를 작성하는 데 시간을 쓰는 것을 원하지 않는다.)
  • 프로그래머들은 다 같다. (프로그래머들도 천차만별로 다르다.)
  • 지연되는 프로젝트에 새로운 자원을 투입해서 진척 속도를 높일 수 있다. (새로 들어온 사람들과 대화하는 데 드는 비용은 거의 항상 도움이 되기보다는 부담이 된다.)
  • 소프트웨어 개발 시간을 확실히 추정하는 것이 가능하다. (이론적으로도 불가능하다.)
  • 프로그래머의 생산성은 코드의 줄 수와 같은 간단한 척도로 측정될 수 있다. (간결한 것을 중시한다면 코드의 줄 수가 늘어나는 것은 좋은 것이 아니라 나쁜 것이다.) 

기회가 된다면 이러한 것들을 설명해 볼 수는 있을 것이다. 하지만 실패하더라고 기분나빠 하지 말고, 이런 신화들에 호전적으로 맞서다가 자신의 평판이 나빠지게 하지도 말라. 이러한 신화들 때문에 관리책임자들은 현재 진행되는 프로젝트에 대해서 실제로 통제력을 가지고 있다는 생각을 더욱 굳힌다. 결국 관리책임자가 좋은 사람이라면 도움이 될 것이고, 안 좋은 사람이라면 방해가 될 것이라는 것이 진리이다.

4.3.11 조직의 일시적 혼돈 상태를 다루는 방법 

조직이 짧은 시기 동안 큰 혼돈을 겪는 때가 종종 있다. 예를 들어, 근신(layoff), 기업 인수(buyout), 회사 공개(IPO, Initial Public Offering), 해고, 신규 채용 등이 그것이다. 이것은 모든 사람의 마음을 어지럽게 하지만, 자기의 지위가 아닌 능력에 근거하여 개인적 자부심을 가지고 있는 프로그래머는 어지러움이 조금 덜할 것이다. 조직의 혼돈은 프로그래머들이 자신의 마법의 능력을 시험해 볼 수 있는 좋은 기회이다. 나는 이 이야기를 끝까지 아껴 두었다. 이것은 우리 부족(tribe)의 은밀한 비밀이기 때문이다. 프로그래머가 아닌 사람은 이제 그만 읽기를 바란다.

기술자들은 만들고 유지하는 능력이 있다. 


전형적인 소프트웨어 회사의 경우, 비기술자들이 주변 사람들에게 지시할 수는 있지만, 기술자 없이는 아무것도 만들거나 유지할 수 없다. 이는 기술자가 일반적으로 제품을 팔거나 사업을 효과적으로 운영할 수 없는 것과 같다. 기술자들의 이 능력은 일시적인 조직의 혼란(mayhem)에 관한 거의 모든 문제들에 대해 버틸 수 있는 힘이 된다. 이런 능력을 가졌다면 이 혼돈을 완전히 무시하고 마치 아무것도 일어나지 않은 것처럼 행동하면 된다. 물론 자신이 해고되는 경우도 있겠지만 이 마법의 능력 때문에 곧 새로운 직장을 찾게 될 것이다. 더욱 흔하게는, 마법의 능력도 없이 스트레스를 받고 있는 사람이 내 자리에 와서 어떤 어리석은 일을 하라고 할 수도 있다. 그 일이 정말로 어리석다고 확신한다면 그 사람 앞에서 그냥 웃으면서 고개를 끄덕이고 회사를 위해서 가장 좋다고 생각되는 다른 일을 하는게 최선이다.

만약 팀장이라면, 팀원들에게도 같은 식으로 하라고 말해 주고, 다른 누가 무슨 말을 해도 무시하라고 말해 주라. 이런 행동 방식이 나 개인에게도 최선이고, 회사와 프로젝트에도 최선이다.

5 참고 문헌 

5.1 책 

<Rules00> Guy Kawasaki, Michelle Moreno, and Gary Kawasaki. 2000. HarperBusiness. Rules for Revolutionaries: The Capitalist Manifesto for Creating and Marketing New Products and Services.

<RDev96> Steve McConnell. 1996. Microsoft Press. Redmond, Wash. Rapid Development: Taming Wild Software Schedules.

<CodeC93> Steve McConnell. 1993. Microsoft Press. Redmond, Wash. Code Complete.

<XP99> Kent Beck. 1999. 0201616416. Addison-Wesley. Extreme Programming Explained: Embrace Change.

<PlanXP00> Kent Beck and Martin Fowler. 2000. 0201710919. Addison-Wesley. Planning Extreme Programming.

<Prag99> Andrew Hunt, David Thomas, and Ward Cunningham. 1999. 020161622X. Addison-Wesley. The Pragmatic Programmer: From Journeyman to Master.

<Stronger> Friedrich Nietzsche. 1889. Twilight of the Idols, "Maxims and Arrows", section 8.

5.2 웹 사이트 

<PGSite> Paul Graham. 2002. 그의 사이트에 있는 논설(article)들: http://www.paulgraham.com/articles.html 모두 읽을 만하지만, 특히 "Beating the Averages".

<Hacker> Eric S. Raymond. 2003. How to Become a Hacker. http://www.catb.org/~esr/faqs/hacker-howto.html.

<HackDict> Eric S. Raymond. 2003. The New Hacker Dictionary. http://catb.org/esr/jargon/jargon.html

<ExpCS> Edsger W. Dijkstra. 1986. How Experimental is Computing Science?http://www.cs.utexas.edu/users/EWD/ewd09xx/EWD988a.PDF

<Knife> Edsger W. Dijkstra. 1984. On a Cultural Gap. http://www.cs.utexas.edu/users/EWD/ewd09xx/EWD913.PDF

6 역사 (2003년 5월 현재) / History (As Of May, 2003) 

6.1 피드백 및 확장 요청 / Request for Feedback or Extension 

이 에세이에 대한 의견이 있다면 나에게 보내주십시오. 나는 모든 의견을 반영하려고 노력하고 있으며 많은 수의 의견들이 이 에세이를 발전시켰습니다.

이 에세이는 GNU Free Documentation License를 따릅니다. 이 라이센스는 에세이에만 적용되는 것이 아닙니다. 에세이는 보통 한 사람의 하나의 관점에 바탕을 두고 쓰여져서 특정한 주장에 집착하고 그것을 확신시키려는 경향이 있습니다. 나는 이 에세이가 쉽고 즐겁게 읽힐 수 있었으면 합니다.

또한 나는 이것이 교육적이었으면 합니다. 비록 교과서는 아닐지라도 이것은 많은 단락으로 나뉘어 있어서 쉽게 새로운 단락이 추가될 수 있습니다. 너무 한쪽 시각으로 편중되었다고 생각한다면 올바르다고 생각되는 쪽으로 이 에세이에 추가하십시오. 이것은 이 라이센스의 목적이기도 합니다.

이 문서가 확장될 가치가 있다고 생각하는 것이 너무 잘난 척 하는 것 같기도 하지만, 이것이 영원히 진화됐으면 합니다. 이것이 다음과 같은 방식으로 확장된다면 기쁘겠습니다.
  • 쉽게 읽을 수 있는 목록이 각 단락에 추가됨.
  • 더 많은, 더 좋은 단락이 추가됨.
  • 비록 단락 단위로 번역되는 한이 있더라도 다른 언어로 번역됨.
  • 틀린 점이나 보충할 것이 추가됨.
  • 팜(PDA의 한 종류) 문서나 더 나은 HTML 같이 다른 문서 형식으로 변환될 수 있게 됨. 

만약 이러한 일을 나에게 알려 준다면 이 라이센스(GFDL)의 목적에 따라서 다음 버전에 추가하겠습니다. 물론 이 라이센스에 나와 있는 대로, 나에게 알리지 않아도 이 문서에 대한 자기만의 버전을 만들 수 있습니다.

감사합니다.

로버트 L. 리드(Robert L. Read)

6.2 원본 / Original Version 

이 문서의 원본은 로버트 L. 리드(Robert L. Read)에 의해 2000년부터 시작되었고 2002년에 사미즈다트(Samizdat) 출판사에서 최초로 전자 문서로 출판되었다.(http://Samizdat.mines.edu) Hire.com의 프로그래머들에게 이 문서를 바친다.

2003년 슬래쉬닷(Slashdot)에서 이 기사가 언급되었고 약 75명의 사람들이 제안과 수정할 것들을 이메일로 나에게 보내왔다. 그것에 감사한다. 비록 많은 중복이 있지만 다음에 열거된 사람들은 커다란 제안을 했거나 문제점을 고치도록 도움을 준 사람들이다. 모건 맥과이어(Morgan McGuire), 데이빗 메이슨(David Mason), 톰 뫼르텔(Tom Moertel), 슬래쉬닷의 닌자 프로그래머(Ninja Programmer) (145252), 벤 비어크(Ben Vierck), 롭 하퍼닉(Rob Hafernik), 마크 하우(Mark Howe), 피터 파라이트(Pieter Pareit), 브라이언 그레이슨(Brian Grayson), 제드 A. 쇼어(Zed A. Shaw), 스티브 벤즈(Steve Benz), 막심 이오프(Maksim Ioffe), 앤드류 우(Andrew Wu), 데이빗 제쉬키(David Jeschke), 톰 코코런(Tom Corcoran).

마지막으로 크리스티나 밸러리(Christina Vallery)에게 감사를 드린다. 그의 수정과 사려깊은 읽기를 통해서 두번째 원고가 크게 발전했다. 그리고 웨인 알렌(Wayne Allen)에게 이 일을 시작하도록 격려해 준 것에 대해서도 감사 드린다.

[edit]

6.3 원저자의 경력 / Original Author's Bio 

로버트 L. 리드(Robert L. Read)는 미국 텍사스주 오스틴에서 부인과 두 아이와 함께 살고 있다. 현재 Hire.com에서 4년 동안 총수석 엔지니어로 일하고 있다. 그전에는 제지 산업 분야에서 스캐너로부터 이미지를 읽어 그것의 품질을 조절해주는 도구를 생산하는 4R Technology를 설립하기도 했다.

그는 1995년 텍사스 주립 대학에서 데이터베이스 이론에 관한 연구로 전산 과학 박사 학위를 받았다. 1987년에는 라이스(Rice) 대학에서 전산 과학 학사 학위를 받았다. 그는 16세 때부터 직업 프로그래머로 일하고 있다.