기술 부채에도 이자가 붙을까
목차10

필자는 개발자가 되기 전에 자동차 부품을 찍어내는 사출 금형을 설계했다. 금형은 쇠를 깎아서 만드는 틀이라서, 한번 깎고 나면 고치는 일이 꽤 비싸다. 그래서 도면 단계에서 몇 번이고 검토를 거치고, 웬만하면 처음부터 제대로 만들려고 애를 쓴다.
소프트웨어로 넘어와서 가장 낯설었던 건 반대로 고치는 비용이 너무 싸 보인다는 점이었다. 쇠를 깎을 필요 없이 몇 줄만 바꾸면 되니까 "일단 돌아가게 만들고 나중에 고치자"라는 말이 아주 자연스럽게 나온다. 그리고 우리 모두 알고 있듯이, 그 나중은 생각보다 비싼 청구서를 들고 찾아온다.
우리는 이 청구서를 기술 부채라고 부른다. 그런데 기술 부채라는 말을 쓸 때 우리가 정말로 무엇을 빌렸고, 이자가 어떻게 불어나는지까지 따져보는 경우는 생각보다 드물다. 보통은 "대충 짠 코드"를 조금 점잖게 부르는 말 정도로 쓰고 지나간다.
그래서 이번 포스팅에서는 기술 부채라는 비유를 금융의 말 그대로 한번 계산해보려고 한다. 이 비유가 처음에 무슨 뜻이었는지, 이자는 어디서 생기고 왜 복리처럼 불어나는지, 그리고 그 이자를 줄이려면 매일 무엇을 해야 하는지를 차례대로 이야기해보자.
처음에 빌린 것은 코드가 아니었다
기술 부채라는 말은 1992년 워드 커닝햄(Ward Cunningham)이 OOPSLA 학회에 낸 짧은 경험 보고서에서 처음 등장했다. 커닝햄의 팀은 WyCash 라는 금융 포트폴리오 관리 시스템을 만들고 있었는데, 금융 소프트웨어를 만드는 팀이었으니 상사에게 리팩터링이 왜 필요한지 설명할 때 금융의 언어를 빌려온 것이다.
여기서 재미있는 건, 이 비유가 원래 "지저분한 코드를 써도 된다"는 뜻이 아니었다는 점이다. 커닝햄은 2009년 이 비유를 다시 설명하면서, 블로그들이 이 개념을 일부러 대충 짠 코드와 헷갈리고 있다고 아쉬움을 표했다. 그가 말한 요지는 이렇다.
코드를 대충 쓰는 것에 찬성한 적은 없다. 다만 문제에 대한 지금의 이해가 부분적이더라도, 그 이해를 반영한 코드를 쓰는 것에는 찬성한다.
즉, 원래 의미의 부채는 "덜 이해한 상태로 일단 내보낸 코드"와 "나중에 더 깊어진 이해" 사이의 간극이다. 처음 빌린 건 코드 품질이 아니라 시간이었고, 그 시간 동안 우리는 문제를 더 잘 이해하게 된다. 그 이해를 다시 코드에 반영하는 것이 빚을 갚는 일이고, 반영하지 않고 그대로 두면 코드와 이해 사이의 어긋남에 계속 걸려 넘어지게 된다. 커닝햄은 바로 그 걸려 넘어지는 비용을 이자라고 불렀다.
이렇게 보면 기술 부채는 실력이 부족한 팀만 지는 빚이 아니다. 오히려 빠르게 배우는 팀일수록 더 자주 지게 되는 빚에 가깝다. 어제의 이해로 짠 코드는 오늘의 이해 앞에서 언제나 조금씩 낡아 보이기 마련이니까.
빚의 수학
그렇다면 이 이자는 얼마나 불어날까. 금융에서 가장 유명한 공식 하나를 그대로 빌려와보자.
여기서 는 원금, 즉 지금 당장 제대로 고친다면 드는 비용이다. 은 스프린트마다 붙는 이자율이고, 는 갚지 않고 버틴 스프린트 수다. 는 스프린트 뒤에 그 빚을 갚으려면 실제로 드는 비용이 된다.
숫자를 넣어보면 감이 온다. 지금 고치면 이틀이면 끝날 구조 문제가 있다고 해보자. 그런데 이 문제를 피해 가려고 매 스프린트 다른 코드들이 조금씩 우회 로직을 덧붙이고, 그 덕분에 나중에 고칠 범위가 스프린트마다 10%씩 늘어난다고 가정해보자.
// 원금, 이자율, 기간을 받아 갚아야 할 비용을 계산
function debtCost(principalDays: number, ratePerSprint: number, sprints: number) {
return principalDays * (1 + ratePerSprint) ** sprints;
}
debtCost(2, 0.1, 0); // 2일
debtCost(2, 0.1, 6); // 약 3.5일
debtCost(2, 0.1, 12); // 약 6.3일
debtCost(2, 0.1, 24); // 약 19.7일6개월 정도인 12 스프린트를 버티면 이틀짜리 일이 엿새짜리가 되고, 1년을 버티면 거의 한 달짜리 일이 된다. 공식은 고등학교 수학 교과서에 나오는 그 복리 공식이 전부인데, 그게 우리 백로그 안에서 조용히 돌아가고 있었던 셈이다.
위 그림에서 눈여겨볼 부분은 곡선의 오른쪽 끝이 아니라 왼쪽이다. 처음 몇 스프린트 동안은 갚는 경우와 미루는 경우의 차이가 거의 보이지 않는다. 복리의 무서운 점은 이자가 크다는 게 아니라, 초반에는 이자가 붙는 게 눈에 잘 띄지 않는다는 데 있다.
왜 단리가 아니라 복리일까
여기서 하나 짚고 넘어가야 할 부분이 있다. 왜 이자가 단리가 아니라 복리처럼 불어날까? 이유는 우회 로직이 우회 로직 위에 쌓이기 때문이다.
처음 구조 문제를 피해 가려고 작성한 코드 A 가 있다고 해보자. 다음 스프린트에서 누군가 A 를 그대로 믿고 그 위에 B 를 짓는다. 그 다음엔 B 위에 C 가 올라간다. 이제 처음 문제를 고치려면 원래 코드뿐 아니라 A, B, C 까지 함께 고쳐야 한다. 이자에 다시 이자가 붙는 구조, 즉 복리의 정의 그대로다.
일상에서 비슷한 구조를 찾자면 거짓말이 그렇다. 작은 거짓말 하나를 덮으려면 다른 거짓말이 필요하고, 그 거짓말을 덮으려면 또 다른 거짓말이 필요하다. 처음 거짓말을 바로잡는 비용은 사과 한 번이었지만, 시간이 지날수록 바로잡아야 할 이야기의 개수가 늘어난다. 우회 로직은 코드가 하는 작은 거짓말이고, 그 위에 쌓인 코드들은 그 거짓말을 믿고 지어진 이야기인 셈이다.
물론 실제 코드베이스의 이자가 정확히 일정한 비율로 붙지는 않는다. 어떤 스프린트에는 아무도 그 코드를 건드리지 않고, 어떤 스프린트에는 한꺼번에 의존성이 몰린다. 그래서 위의 계산은 정확한 예측이라기보다 "왜 미룰수록 체감 비용이 가파르게 늘어나는가"를 보여주는 모형에 가깝다.
이자는 어디서 나가는가
금융 부채의 이자는 통장에서 빠져나가니 눈에 보인다. 기술 부채의 이자는 어디에서 빠져나갈까. 필자는 크게 세 군데라고 생각한다.
첫째는 우회 비용이다. 구조가 어긋난 곳을 지날 때마다 우리는 "여기는 원래 이렇게 하면 안 되는데, 저 부분 때문에 어쩔 수 없이"로 시작하는 코드를 짠다. 둘째는 설명 비용이다. 새로 합류한 동료에게 "이 부분은 역사적인 이유가 있어서"를 설명하는 시간도 고스란히 이자다. 셋째는 두려움 비용이다. 건드리면 어디가 터질지 모르는 코드 앞에서 우리는 손을 대지 않는 쪽을 택하고, 그렇게 생긴 우회가 다시 첫째 비용으로 돌아간다.
이 세 가지 이자의 공통점은 팀의 총 시간을 늘리지 않고, 새 기능에 쓸 수 있는 몫만 깎아 먹는다는 것이다. 그래서 기술 부채가 쌓인 팀은 다들 바쁜데 이상하게 결과물이 줄어드는 슬픈 상황을 겪는다. 아무도 놀지 않았는데 이자만 꼬박꼬박 냈던 것이다.
가상의 팀으로 한번 계산해보자. 다섯 명이 2주 스프린트로 일하는 팀이라면, 휴가나 회의를 빼고 대략 스프린트당 40일 정도를 개발에 쓸 수 있다. 첫 스프린트에 이자로 나가는 시간이 4일이라면 새 기능에는 36일을 쓴다. 그런데 이자가 매 스프린트 20%씩 늘어난다면, 여섯 번째 스프린트에는 이자가 약 10일, 열두 번째 스프린트에는 약 30일이 된다.
// 팀 가용 시간에서 이자를 뺀 새 기능 시간
const capacity = 40;
const interest = (sprint: number) => 4 * 1.2 ** (sprint - 1);
for (const sprint of [1, 6, 12]) {
console.log(sprint, Math.round(capacity - interest(sprint)));
}
// 1 36
// 6 30
// 12 10열두 번째 스프린트의 이 팀은 여전히 다섯 명이 매일 출근해서 열심히 일한다. 하지만 새 기능에 쓰는 시간은 처음의 4분의 1 수준이다. 밖에서 보면 팀이 갑자기 느려진 것처럼 보이지만, 사실은 1년 동안 미뤄둔 이자를 한꺼번에 내고 있는 중인 것이다. (물론 이 상황에서 나오는 해결책은 보통 "사람을 더 뽑자"인데, 그 이야기는 또 다른 포스팅 거리다)
깨진 창문과 소프트웨어 엔트로피
1982년 범죄학자 제임스 윌슨(James Q. Wilson)과 조지 켈링(George L. Kelling)은 깨진 창문이라는 글에서, 깨진 창문 하나를 방치한 건물은 곧 다른 창문도 깨진다는 가설을 내놓았다. 앤드류 헌트(Andrew Hunt)와 데이비드 토머스(David Thomas)는 1999년 『실용주의 프로그래머』에서 이 비유를 코드로 가져와 "깨진 창문을 방치하지 말라"고 썼다.
코드베이스에서 이 현상은 꽤 쉽게 관찰된다. 이미 우회 로직이 잔뜩 있는 파일에 새 코드를 추가하는 사람은 "여기는 원래 이런 곳이니까" 하고 우회 로직을 하나 더 얹는다. 반대로 깔끔하게 정돈된 파일에서는 지저분한 코드를 추가하는 것 자체가 눈치 보이는 일이 된다. 첫 번째 우회가 다음 우회의 허락이 되는 셈이다.
소프트웨어 진화를 연구한 메이어 레만(Meir M. Lehman)은 1980년 소프트웨어 진화의 법칙을 정리하면서 이런 경향을 법칙으로 적었다. 계속 바뀌는 프로그램의 복잡도는 그것을 유지하거나 줄이려는 작업을 따로 하지 않는 한 계속 늘어난다는 것이다. 열역학에서 아무것도 하지 않으면 엔트로피가 늘어나듯, 코드도 아무것도 하지 않으면 저절로 정돈되는 일은 없다.
다만 깨진 창문 이론은 원래의 범죄학 분야에서는 논쟁이 많은 이론이라는 점도 함께 적어두고 싶다. 이 이론을 바탕으로 한 치안 정책의 효과와 부작용은 지금도 연구자들 사이에서 의견이 갈린다. 그래서 필자는 이 이야기를 사회 이론의 증거가 아니라, 코드베이스에서 우리가 경험하는 현상을 설명하는 비유로만 쓰려고 한다.
좋은 빚과 나쁜 빚
여기까지 읽으면 기술 부채는 무조건 피해야 하는 것처럼 들린다. 하지만 금융에서도 모든 빚이 나쁜 건 아니다. 집을 사려고 받는 대출과 충동구매로 긁은 카드 할부는 둘 다 빚이지만 성격이 꽤 다르다.
마틴 파울러(Martin Fowler)는 2009년 기술 부채 사분면이라는 글에서 빚을 두 개의 축으로 나눴다. 하나는 무모하게 졌는지 신중하게 졌는지이고, 다른 하나는 의도하고 졌는지 모르고 졌는지다.
이 중에서 파울러가 가장 흥미롭게 짚는 칸은 오른쪽 아래, 신중하지만 의도하지 않은 빚이다. 좋은 팀이 최선을 다해 설계했는데도, 일을 마치고 나서야 "아, 이렇게 했어야 했구나"를 깨닫는 경우다. 파울러는 이 빚을 실수가 아니라 배움의 자연스러운 결과로 본다. 앞에서 이야기한 커닝햄의 원래 의미와 정확히 같은 지점이다.
반대로 왼쪽 위의 무모하고 의도적인 빚, 즉 "설계할 시간이 없다"며 지는 빚은 대부분 이자율을 과소평가해서 생긴다. 앞의 복리 그래프에서 초반 곡선이 평평해 보이는 착시가 바로 이 칸의 결정을 부추긴다.
그렇다면 오른쪽 위, 신중하고 의도적인 빚은 어떤 모습일까. 예를 들어 다음 주 월요일에 마케팅 캠페인이 시작되고, 그 전에 결제 화면을 반드시 내보내야 하는 상황을 떠올려보자. 팀은 결제 수단 확장을 고려한 구조가 필요하다는 걸 알고 있지만, 지금은 카드 결제 하나만 하드코딩하기로 결정한다. 대신 그 결정을 이슈에 남기고, 캠페인이 끝난 다음 스프린트에 구조를 다시 잡는 일정을 미리 잡아둔다.
같은 하드코딩이라도 이 팀의 빚은 왼쪽 위의 빚과 성격이 다르다. 빌린 이유와 갚을 시점이 적혀 있고, 이자가 붙는 기간도 한 스프린트로 정해져 있다. 금융으로 치면 상환 계획이 있는 단기 대출에 가깝다.
빚을 갚는 순서
빚을 갚기로 마음먹었다면 다음 고민은 순서다. 개인 재무에서 여러 빚을 갚는 방법으로 자주 소개되는 전략이 두 가지 있다. 이자율이 가장 높은 빚부터 갚는 눈사태(avalanche) 방식과, 잔액이 가장 작은 빚부터 갚아서 성취감을 쌓는 눈덩이(snowball) 방식이다. 수학적으로는 눈사태 방식이 총이자를 가장 적게 내는 방법이다.
기술 부채에 눈사태 방식을 적용하려면 빚마다 이자율을 알아야 한다. 앞에서 는 지금 고치는 비용, 은 스프린트마다 붙는 이자율이라고 했다. 그렇다면 같은 시간을 들였을 때 가장 많은 이자를 줄여주는 빚부터 갚는 것이 합리적이다.
는 갚는 데 드는 비용 하루당 줄일 수 있는 이자율이다. 고치는 데 하루밖에 안 걸리는데 매 스프린트 이자가 크게 붙는 빚은 가 크고, 고치는 데 한 달이 걸리는데 거의 아무도 건드리지 않는 코드의 빚은 가 작다.
문제는 이자율 을 어떻게 추정하느냐다. 여기서 쓸 만한 힌트를 주는 사람이 애덤 톤힐(Adam Tornhill)이다. 그는 『Your Code as a Crime Scene』(2015)에서 자주 바뀌면서 동시에 복잡한 파일을 핫스폿이라고 부르고, 결함과 병목이 이런 곳에 몰리는 경향이 있다고 설명했다. 이자는 결국 누군가 그 코드를 건드릴 때 나가는 것이니, 변경 빈도는 이자율의 꽤 괜찮은 대리 지표가 된다.
# 최근 1년간 가장 자주 바뀐 파일 상위 10개
git log --since="1 year ago" --name-only --format="" | sort | uniq -c | sort -rn | head -10이 명령의 결과와 "이 파일을 고칠 때마다 괴로운가"라는 팀의 감각을 겹쳐보면, 이자율이 높은 빚의 후보가 꽤 정확하게 나온다. 반대로 3년 동안 한 번도 바뀌지 않은 지저분한 파일은 보기에 불편할 뿐, 이자는 거의 나가지 않는 빚일 가능성이 크다. 톤힐 본인도 이런 지표가 정확한 측정이 아니라 문제가 몰려 있을 확률을 알려주는 휴리스틱일 뿐이라고 강조한다는 점은 함께 기억해두자.
그래서 만능일까
이쯤 되면 이런 반론이 나올 수 있다.
그래서 결국 부채는 무조건 빨리 갚으라는 이야기인가?
맞다. 사실 이자율이 높은 빚은 빨리 갚을수록 이득이다. 하지만 금융 비유를 끝까지 밀고 가면, 갚지 않아도 되는 빚도 있다는 걸 인정해야 한다.
소프트웨어의 빚은 그 코드가 사라지면 함께 사라진다. 2주 뒤에 버릴 실험용 기능, 캠페인이 끝나면 지울 이벤트 페이지에 지는 빚은 이자가 붙기 전에 원금째 소멸한다. 이런 코드를 깔끔하게 다듬는 데 시간을 쓰는 건, 다음 달에 해지할 대출을 미리 갚느라 생활비를 쓰는 것에 가깝다.
부채라는 단어가 주는 착시도 있다. 부채라고 부르는 순간 "언젠가 갚으면 되는 것"이라는 안도감이 함께 따라온다. 하지만 실제로 갚아본 팀은 알겠지만, 빚을 갚을 수 있는 시점은 생각보다 자주 오지 않는다. 조직이 커지고 그 코드에 기대는 기능이 늘어날수록, 원금을 한 번에 갚을 수 있는 창문은 점점 좁아진다. 결국 갚지 못한 빚은 부채라기보다 그냥 코드베이스의 지형이 되어버린다.
또 하나의 한계는 측정이다. 금융 부채는 원금과 이자율이 계약서에 적혀 있지만, 기술 부채의 와 은 어디에도 적혀 있지 않다. 그래서 위의 공식은 계산기라기보다, 팀이 빚에 대해 같은 언어로 이야기하게 해주는 도구로 쓰는 편이 정직하다.
이자를 줄이는 습관
그렇다면 매일의 개발에서 무엇을 하면 좋을까. 거창한 리팩터링 프로젝트보다 효과가 큰 건 의외로 작은 습관들이다.
첫 번째는 보이스카우트 규칙이다. 로버트 마틴(Robert C. Martin)이 『클린 코드』(2008)에서 소개한 이 규칙은 "캠프장을 처음 왔을 때보다 깨끗하게 해놓고 떠나라"는 것이다. 지나가는 길에 깨진 창문 하나씩만 고쳐도, 이자율 자체를 낮추는 효과가 있다.
두 번째는 빚을 질 때 차용증을 남기는 것이다. 의도적으로 빚을 진다면, 왜 졌는지와 언제쯤 갚을지를 코드 근처나 이슈에 적어두자. 차용증이 없는 빚은 시간이 지나면 아무도 빚인 줄 모르는 "원래 그런 코드"가 된다. (그리고 원래 그런 코드는 원래 그런 채로 10년을 산다)
세 번째는 이자를 팀의 언어로 만드는 것이다. "이 부분 코드가 좀 별로예요"라는 말보다 "이 부분 때문에 매 스프린트 이틀씩 우회 비용이 나가고 있어요"라는 말이 훨씬 설득력이 있다. 앞에서 계산해본 것처럼, 부채를 숫자로 이야기하기 시작하면 갚을지 말지를 감이 아니라 비용으로 결정할 수 있게 된다.
마치며
처음 이야기로 돌아가 보자. 금형은 고치는 비용이 비싸 보이기 때문에 사람들이 처음부터 조심한다. 반면 코드는 고치는 비용이 싸 보이기 때문에 우리는 미루는 데 익숙해진다. 그런데 사실 그 싸 보이는 비용에는 매 스프린트 복리 이자가 붙고 있었다.
그래서 필자는 "기술 부채를 없앨 수 있을까"라는 질문이 사실 "우리는 지금 어떤 이자를 내고 있는지 알고 있을까"라는 질문에 더 가깝다고 생각한다. 빚이 있다는 사실보다 위험한 건, 이자를 내고 있다는 사실을 모르는 것이기 때문이다.
다음에 "일단 돌아가게 만들고 나중에 고치자"라는 말이 나온다면, 그 나중이 몇 스프린트 뒤인지, 그 사이에 이자율은 얼마일지를 한 번만 같이 계산해보자. 생각보다 많은 경우에 지금 갚는 쪽이 싸다.
이상으로 기술 부채에도 이자가 붙을까 포스팅을 마친다.