제품을 만든다는 건 kludge를 쌓는 일이다

제품을 만든다는 건 kludge를 쌓는 일이다

마감을 이틀 앞두고, 고객 하나가 자기네 화면만 다르게 보여달라고 한다. 처음 기획에는 없던 요구다. 초기 설계 그대로는 대응할 수 없는 지점에 부딪힌 거다. 제대로 하려면 설정 구조를 갈아엎어야 하는데, 그럴 시간은 없다. 그래서 딱 그 고객만 걸러내는 조건문 한 줄을 슬쩍 끼워 넣는다. 돌아간다. 배포한다. 그리고 그 한 줄은 그대로 남는다.

이런 걸 kludge라고 부른다. 1962년에 잭슨 그랜홈(Jackson Granholm)이 처음 활자로 옮긴 말인데, 그때 붙인 정의가 재밌다. 서로 안 맞는 부품들을 그러모아 만든, 어딘가 영 마음에 안 드는 전체. 어원은 독일어 klug, 영리하다는 뜻이다. 영리함에서 따온 단어가 정작 가리키는 게 엉성한 땜빵이라는 게 이 말의 첫 번째 아이러니다.

한동안 나는 kludge를 실력의 문제라고 여겼다. 설계를 제대로 했으면 안 생겼을, 부끄러운 흔적. 그런데 제품을 몇 개 만들어보고 생각이 바뀌었다. kludge는 실력이 모자라서가 아니라, 초기 기획으로는 대응할 수 없는 지점이 반드시 생기기 때문에 나온다. 제품을 만든다는 행위 자체가, 그 지점마다 반응해서 kludge를 쌓아가는 일이다. 이건 결함이 아니라 구조다. 제품이 큰다는 건 이렇게 진화한다는 뜻이고, kludge는 그 진화의 자연스러운 산물이다.

물론 이게 대충 만들어도 된다는 말은 아니다. 당연히 챙겼어야 할 것, 처음부터 알 수 있었고 알았어야 할 걸 빠뜨린 건 kludge가 아니라 그냥 못 만든 거다. kludge는 초기 기획으로는 미리 알 수 없던 지점에 부딪혀, 그걸 알면서 일부러 싸게 넘긴 선택이다. 몰라서 빠뜨린 것과 알고도 미뤄둔 것은 전혀 다르다.

설계자라는 착각

제품을 처음 시작할 때 나는 설계도부터 그리려고 했다. 기능을 다 적고, 화면을 다 그리고, 예외를 다 세어두면 그다음은 조립이라고 믿었다. 그런데 애자일(agile)하게 굴러갈수록 그 믿음이 깨졌다. 문제는 설계가 어렵다는 게 아니었다. 다 설계한다는 것 자체가 원리적으로 불가능하다는 거였다.

왜냐고? 제품을 둘러싼 환경이 가만히 있질 않으니까. 고객이 바뀌고, 고객이 원하는 게 바뀌고, 옆 회사가 뭔가 내놓으면 기준이 통째로 바뀐다. 내가 완벽한 설계도를 그리는 동안, 그 설계도가 겨누던 과녁은 이미 다른 자리로 옮겨가 있다. 움직이는 과녁을 미리 다 맞히는 설계도라는 건 존재할 수가 없다. 그러니 착수 시점에 모든 걸 고려한다는 건 성실함의 문제가 아니라, 애초에 성립하지 않는 목표다.

제품은 설계되지 않고 진화한다

이 감각에 이름을 붙여준 건 엉뚱하게도 생물학이었다. 1977년에 분자생물학자 프랑수아 자코브(François Jacob)가 쓴 글이 있다. 요지는 이렇다. 진화는 설계자가 아니라 땜장이(bricoleur)처럼 일한다. 설계자는 목표를 정하고, 필요한 재료를 새로 구해서, 설계도대로 만든다. 진화는 그렇게 안 한다. 눈앞에 굴러다니는 것들, 이미 있는 부품들을 주워다가 그때그때 쓸 만하게 얽어서 문제를 넘긴다. 앞을 내다보지 않고, 지금 닥친 환경에 반응할 뿐이다.

제품도 정확히 그렇다. 나는 제품의 설계자가 아니라 환경을 읽고 반응하는 사람이다. 고객이 뭔가를 불편해하면 거기 반응해서 뭘 붙이고, 요구가 바뀌면 또 거기 맞춰 얽는다. 그 반응 하나하나가 kludge다. 생물이 환경 압력에 대응하며 지금 모양이 된 것처럼, 제품도 이 반응들이 쌓여서 지금 모양이 된다. 진화압이 고객과 요구의 변화라면, kludge는 그 압력에 대응한 흔적이다.

여기까지 오면 kludge를 보는 눈이 좀 달라진다. 지저분한 땜빵 하나하나가, 사실은 제품이 현실과 부딪혀서 살아남은 자국이라는 거니까.

진화는 쉽게 되돌리지 못한다

그런데 진화에는 고약한 성질이 하나 있다. 백지에서 다시 그리질 못한다는 거다. 이미 만들어진 것 위에 얹기만 할 수 있다.

기린 목을 보자. 뇌에서 나와 후두로 가는 신경이 하나 있는데(되돌이 후두신경), 이게 바로 옆 후두로 곧장 안 가고 목을 타고 심장 근처까지 쭉 내려갔다가 다시 목 위로 되돌아온다. 기린은 이 우회로가 4미터가 넘는다. 물고기이던 먼 조상 때는 이 경로가 곧게 뻗은 지름길이었다. 그런데 목이 길어지고 심장이 아래로 내려가는 동안, 신경이 하필 심장 대동맥의 반대편에 걸려버렸다. 진화는 이걸 끊어서 새로 잇지 못했다. 그냥 조금씩 늘렸다. 그래서 아직도 그 말도 안 되는 우회로가 남아 있다. 옛 환경에선 최선이었는데 지금은 걷어내지 못한 흔적, 딱 kludge다.

제품에도 이런 흔적기관이 쌓인다. 옛날 고객, 옛날 요구에 반응해서 붙였는데 환경이 바뀐 지금은 아무도 안 쓰는, 그런데 무서워서 못 지우는 코드. 제품이 오래될수록 이런 게 지층처럼 깔린다.

여기서 생물과 제품이 갈라진다. 기린은 신경을 리셋할 수 없지만, 나는 제품을 갈아엎을 수 있다. 마음먹으면 그 지층을 다 걷어내고 처음부터 다시 그릴 수 있다. 그러면 kludge 없는 깨끗한 제품이 나올까?

어느 쪽이든 빚이다

kludge를 얹으면 지저분함이 쌓인다. 다음에 그 위에 뭘 올릴 때마다 조금씩 더 힘들어진다. 이건 갚아야 할 빚이다. 흔히 기술 부채(technical debt)라고 부르는 그것이다.

그럼 갈아엎으면? 갈아엎는 데는 노동력이 든다. 멀쩡히 돌아가던 걸 멈추고, 사람을 갈아 넣고, 그동안 새 기능은 못 만든다. 그것도 빚이다. 단지 지저분함이라는 형태가 아니라 시간과 노동이라는 형태로 치를 뿐이다.

그러니까 결론은 이렇다. kludge는 빚을 안 지는 길이 아니다. 지금 당장 제일 싸게 치르는 길이다. 어느 쪽을 골라도 빚은 못 피한다. 제품을 만든다는 건 어떤 형태의 빚을 질지 계속 고르는 일이지, 빚 없는 상태에 도달하는 일이 아니다.

PM: “이거 이번 고객만 예외로 막아둘 수 있어?” 개발자: “되긴 하는데, 나중에 이거 건드리기 싫어질 거예요.”

이 짧은 대화 안에 두 장의 카드가 다 들어 있다. 지금 싸게 치를 거냐, 나중에 비싸게 제대로 할 거냐. 정답이 정해져 있는 질문이 아니다.

정답은 미리 정해지지 않는다

그럼 뭘 보고 고르나. 이 부분은 땜빵으로 넘기고 저 부분은 멈춰서 제대로 하고, 이걸 미리 판단하는 규칙이 있으면 좋겠는데 없다.

없을 수밖에 없다. 어떤 kludge가 옳은 선택이었는지는, 제품이 그다음 환경 변화를 한 번 견뎌내고 난 뒤에야 드러나기 때문이다. 대충 막아둔 그 한 줄이 알고 보니 아무도 안 건드리는 안정된 자리였다면, 그건 영원히 싸게 끝난 좋은 kludge다. 반대로 대충 막아둔 자리가 하필 제품의 심장이어서 그 위에 온갖 게 쌓여버리면, 그 빚은 이자가 붙어 돌아온다. 그런데 그 자리가 심장인지 변두리인지는, 시간이 지나 환경이 어디를 계속 두드리는지 봐야 알 수 있다.

이건 진화가 적합도를 다루는 방식과 똑같다. 진화는 어떤 형질이 유리한지 미리 알고 설계하지 않는다. 일단 만들어보고, 살아남은 걸 보고 나서야 그게 유리했다는 걸 사후에 안다. 적합도는 예언이 아니라 관찰의 결과다. 제품도 마찬가지다. 어떤 kludge가 자산이고 어떤 kludge가 부채였는지는, 제품이 살아남고 난 뒤의 관찰 결과로만 나온다.

물론 사람은 눈먼 진화와 똑같지는 않다. 경험 많은 엔지니어나 디자이너라면 어디가 심장이 되기 쉬운지를 어느 정도는 미리 짚는다. 결제나 인증, 데이터 모델처럼 위에 뭐든 쌓이게 되는 자리, 요구가 자주 바뀌는 자리는 대충 막아두면 나중에 이자가 붙는다는 걸 여러 번 겪어서 안다. 반대로 한 번 쓰고 버릴 화면이나 실험 삼아 붙인 기능은 땜빵으로 넘겨도 대개 탈이 없다. 그래서 노련할수록 여기는 싸게, 저기는 제대로라는 감이 선다. 다만 이건 확률을 높이는 감이지 정답의 보증은 아니다. 아무리 노련해도 어느 자리가 진짜 심장이 될지는 환경이 그 자리를 실제로 두드려봐야 최종적으로 확정된다. 경험은 사후에야 나오던 답을 조금 앞당겨 짐작하게 해줄 뿐, 답 자체를 미리 만들어내지는 못한다.

그래서 다시 처음 이야기로 돌아온다. 착수 시점에 다 설계할 수 없는 진짜 이유가 이거다. 미래를 못 봐서가 아니라, 정답이라는 게 아직 세상에 존재하지 않기 때문에 미리 못 고르는 거다. 정답은 제품이 환경을 한 번 더 통과한 다음에야 생겨난다.

진짜 위험은 빚이 아니라 kludge를 안 만드는 것

여기까지는 kludge를 얹느냐 갈아엎느냐, 그 사이에서 저울질하는 이야기였다. 어느 쪽이든 빚이 든다는 이야기. 그런데 한 발 물러서서 보면, 그 빚은 제일 위험한 문제가 아니다.

진짜 위험한 건 kludge를 아예 안 만들려는 태도다. 그리고 그건 겉보기엔 정반대인 두 얼굴로 나타난다.

하나는 “제대로 만들어야 한다"며 매번 전체를 갈아엎으려는 쪽이다. 요구가 하나 들어올 때마다 임시로 막고 넘어가는 걸 못 견뎌서, 구조를 통째로 다시 짜야 직성이 풀린다. 완벽하게 짜기 전엔 못 내보낸다는 결벽이다. 다른 하나는 “처음 기획이 정답"이라며 초기 설계를 끝까지 강요하는 쪽이다. 환경이 다른 걸 요구해도, 처음에 이렇게 그렸으니 지켜야 한다며 덧대기를 거부한다.

둘은 정반대로 보이지만 하는 짓은 같다. 둘 다 kludge를 건너뛴다. 임시로 싸게 막아두고 환경을 한 번 더 통과시켜 보는 그 과정을, 한쪽은 과하게 다시 지어서 생략하고 다른 한쪽은 아예 안 움직여서 생략한다. 그런데 kludge는 실패의 흔적이 아니라 제품이 환경에 반응하고 있다는 증거다. 앞에서 봤듯 제품은 진화한다. 진화란 환경이 두드릴 때마다 그 자리에 뭔가를 덧대고 틀어가며 대응하는 것이고, kludge는 그 대응의 다른 이름이다. 그 대응을 생략하면 세상이 움직여도 제품은 처음 그림에 묶여 그대로 멈춘다. 이건 빚이 쌓이는 문제가 아니라 아예 진화를 멈춘 문제다. 빚은 갚으면 되지만, 진화를 멈춘 제품은 조용히 뒤처지다 사라진다.

물론 kludge를 쌓다 보면 부분 땜빵으로는 안 되고 구조 전체를 다시 짜야 하는 순간도 진짜로 온다. 갈아엎기 자체가 문제인 게 아니다. 문제는 그게 환경이 두드려서 필요해진 리팩토링이냐, 아니면 임시방편이 꼴 보기 싫어 미리 당겨온 리팩토링이냐다. 앞엣것은 진화고, 뒤엣것은 결벽이다.

그 결벽과 고집의 뿌리에는 대개 자존심과 불안감이 있다. 완벽하게 짜야 실력이라는 착각, 처음 설계를 끝까지 지켜야 실력이라는 고집이 자존심 쪽이라면, 임시로 싸게 막아둔 흔적을 남이 볼까, 방향을 틀면 처음 판단이 틀렸다고 인정하는 꼴이 될까 겁내는 마음이 불안감 쪽이다. 그런데 환경이 바뀌는 마당에 그것들은 실력이 아니라, 방향을 틀어야 할 때 못 틀게 붙잡는 닻이다.

그래서 어떻게 만드나

이렇게 정리하고 나면 제품을 만드는 자세가 좀 바뀐다.

첫째, kludge를 부끄러워하지 않되 기록은 해둔다. 지금 이건 땜빵이고, 어떤 가정 위에서 싸게 친 거고, 그 가정이 깨지면 갚아야 한다는 걸 남겨둔다. 부끄러워서 숨기면 빚인 줄도 모르고 이자만 불어난다.

둘째, 갈아엎어야 할 때가 오면 미루지 않는다. 아직 그 자리가 심장인지 변두리인지 모를 때 미리 갈아엎는 것도 낭비지만, 환경이 분명하게 여기 다시 지으라고 두드리는데도 초기 설계를 지키겠다고 버티는 건 더 큰 낭비다. 큰 리팩토링은 실패의 증거가 아니라, 제품이 아직 살아서 진화하고 있다는 증거다.

그리고 자세를 바꾸는 김에, 오래 붙들고 있던 질문 하나를 버린다. 초기 설계가 맞았나 틀렸나. 이건 사실 별로 중요한 질문이 아니다. 초기 설계는 그때 가진 정보로 세운 가설일 뿐이고, 환경이 움직이는 이상 언젠가 어긋나는 게 정상이다. 진짜 물어야 할 건 따로 있다. 우리가 그동안 무엇을 배웠나. 그리고 이 제품이 지금 고객에게 실제로 가치를 주고 있나, 돈을 벌고 있나. 초기 설계가 맞았는지가 아니라 이 두 개가 제품의 성적표다.

제품은 설계되는 게 아니라 진화한다. 그리고 진화한다는 건 완벽해진다는 뜻도, 처음 그림을 끝까지 지킨다는 뜻도 아니다. 배운 대로 계속 고쳐 나간다는 뜻이다. 그러니 잘 만든 제품은 kludge가 없는 제품도, 초기 설계를 지켜낸 제품도 아니다. 무엇을 배웠는지 알고, 필요할 때 서슴없이 갈아엎으며, 그렇게 고객에게 값을 하는 제품이다.

이 글, 어떠셨어요?