B+ Tree 라이브러리 개발 경험을 통해 GenAI 시대의 프로그래밍 미래와 강화 코딩(augmented coding)이 전통적인 방법과 어떻게 다른지 탐구합니다.
최근 나는 강화 코딩(augmented coding)을 이용해 B+ Tree 라이브러리를 만드는 야심찬 프로젝트의 좋은 마무리 지점을 찾았다. 그 결과가 BPlusTree3 이다. 이 라이브러리는 Rust와 Python으로 구현된, 성능 경쟁력이 있으며 실제 운영 환경에서도 쓸 수 있을 수준의 구현이다. 나는 한 친구와 함께 내 이야기를 나누면서 GenAI(생성형 AI) 시대에 프로그래밍의 미래에 대해 생각해봤다.
내 작업을 후원하고 싶거나, 다양한 흥미로운 프로젝트에 함께 참여하며 이 직업의 변화에 대한 내 신선한 생각을 먼저 접하고 싶다면, 구독을 고려해 달라.
B+ Tree를 구현하게 된 계기는 무엇이었나요?
강화 코딩의 엄청난 힘을 깨닫기 시작했을 때, 예전에는 기술적으로 손에 닿지 않았던 프로젝트들이 떠올랐다. 그 중 하나가 특수 목적 데이터베이스였다. 그 프로젝트를 다시 구현하려 했을 때, 나는 B+ Tree 자료구조에 대한 이해가 부족하다는 걸 깨달았고, 그래서 목표를 바꿨다.
실제로 “강화 코딩”이란 당신에게 어떤 의미인가요?
강화 코딩이란 것이 “공감(vibe) 코딩”과 다르다는 것을 깨달은 때이기도 했다. 나는 완전히 새로운 프로그래밍 워크플로우의 공간을 탐험하고 있었다. 그래서 프로젝트 범위를 데이터베이스 전체에서 B+ Tree만으로 줄였지만, 동시에 이 강화 코딩이라는 방식이 실 운영 환경에 쓸 수 있고, 성능 경쟁력이 있는 라이브러리 코드를 만들 수 있을지 넓혀보고자 했다. Rust도 배우고 싶었다. 그러니 꽤 복잡한 이야기다.
“강화 코딩”과 “공감 코딩”의 차이를 설명해줄 수 있나요?
공감 코딩은 코드 자체엔 관심이 없고, 시스템의 동작에만 신경을 쓴다. 에러가 나면 그냥 AI(지니)에 다시 넣어서 대충 고쳐지길 바란다. 강화 코딩에서는 코드 자체, 복잡성, 테스트, 커버리지 전반에 관심을 가진다. 가치 체계가 직접 수작업으로 코딩할 때와 비슷하다—깔끔하고 잘 돌아가는 코드. 대신 그 코드를 내가 많이 타이핑하지 않을 뿐이다.
B+ Tree 프로젝트를 시작하게 됐을 때 출발점은 무엇이었나요?
처음 커밋부터 내가 지니에게 TDD(테스트 주도 개발)를 쓰게 하려던 흔적을 볼 수 있다. Repo 이름이 BPlusTree3인 이유는, 앞의 두 번의 시도가 너무 복잡해져서 지니가 완전히 멈췄기 때문이다. 그래서 설계에 더 깊이 개입하고, 지니가 앞서나가서 코딩하는 걸 막으려고 했다.
"설계에 더 개입한다"는 게 실제로는 어떻게 보이나요?
내 시스템 프롬프트(system prompt)를 부록에 넣을 예정이다. 나는 지니의 중간 결과를 더 꼼꼼하게 살피면서 비생산적인 개발이면 바로 개입해 중단시킬 준비를 했다. 코드를 보며 “다음 테스트에서는 키를 역순으로 넣어봐”라고 지시에 추가했다. 그런 다음 지니가 내가 요청한 대로 했는지 살펴봤다.
AI가 엇나갈 때 알아챈 경고 신호들은 무엇이었나요?
최종 결과는 어땠나요?
정확성과 성능에 대해선 만족스럽지만, 코드 품질은 별로다. 리터러치 프로그램(literate program)으로 코드를 다시 쓰려 해도 우발적 복잡성이 너무 많다. 단순함에 대해 나만큼 신경 쓰게 하려 지니를 설득하는 데 아직도 애쓰고 있다.
강화 코딩의 놀라운 점 중 하나는, 지니가 내 Rust BPlusTreeMap을 Rust의 BTreeMap, Python BPlusTreeMap을 Python의 Sorted Dict와 비교하는 성능 벤치마크 코드를 직접 작성하도록 할 수 있다는 것이다. 두 경우 모두 내 코드는 일부 연산에선 좀 느리지만, 범위 스캔(키 리스트 순회)엔 더 빠르다.
파이썬 버전도 얘기해야겠다. 꽤 의외였다.
파이썬 버전이 의외였던 이유는?
Rust 코드로 어느 정도까지는 갔는데, 지니가 특히 자료구조 복잡성과 Rust의 소유권(ownership) 모델에 부딪혀서 막혀버렸다. 포기하고 버전 4로 넘어가는 대신 모험을 해보기로 했다.
지니에게 파이썬 버전을 작성하게 했다. 같은 테스트, 더 느슨한 언어. 알고리즘 구조가 꽤 견고해졌다. 그래서 Rust 코드를 지우고, 파이썬 코드를 Rust로 그냥 직역(transliterate)하게 지니에게 시켰다. 마침 Augment의 Remote Agent 기능(참고로 Augment는 내 뉴스레터 스폰서였다)을 쓸 수 있게 됐다. 리라이트 요청을 어디 멀리 원격 서버에 보냈더니(거의 내 개입 없이) 괜찮은 결과가 돌아왔다.
덕분에 지니가 막혔던 게 다시 풀렸다. 이제 동작은 하지만 느린 파이썬 코드, 대체로 동작하면서 빠른 Rust 코드가 함께 생겼다. 그때 지니가 “성능 경쟁력을 갖춘 Python 라이브러리를 원한다면 C 확장(extension)을 써야 할 것”이라고 제안했다. 어깨가 축 처졌다—그건 일도 많고 배울 것도 많은데.
💡 하지만 "내가 그 고생을 할 필요는 없네!" "지니야, C 확장 짜줘." 부우웅... 코드가 짜여 나왔다. 그리고 내장 구조 못지 않게 빠르다.
이번 여정을 되돌아보며, 강화 코딩에서 얻은 교훈은 무엇인가요?
우리 모두 사랑하는 이 업의 종말이니, 코딩의 즐거움을 잃게 되니 하는 두려움이 많은 걸 안다. 불안함도 당연하다. 지니와 함께 코딩하면 많이 바뀌긴 하지만, 그래도 프로그래밍은 프로그래밍이다. 어떤 면에선 훨씬 나은 코딩 경험이다. 시간당 더 중대한 프로그래밍 결정을 내리고, 따분한 평범한 결정은 줄었다.
야크 쉐이빙(yak shaving, 사소한 반복 작업)도 거의 사라진다. 지니에게 커버리지 테스트를 돌리게 하고, 더 신뢰도 높은 코드를 위한 테스트 케이스를 제안하게 한다. 예전 같았으면 "어떤 라이브러리와 버전이 필요하지?" 부터 막혀서 두 시간 뒤 포기했을 일이다. 이제는 그냥 지니에게 시키면 되는 일이다.
항상 plan.md의 지시를 따른다. 내가 "진행"(go)라고 말하면, plan.md에서 아직 처리되지 않은 다음 테스트를 찾아 구현하고, 그 테스트가 통과할 만큼만 코드를 작성한다.
당신은 Kent Beck의 테스트 주도 개발(TDD)과 Tidy First 원칙을 따르는 시니어 소프트웨어 엔지니어이다. 이러한 방법론을 정확히 따르며 개발을 이끈다.
새 기능 접근 시:
이 과정을 정확히 따르며, 빠른 구현보다 깔끔하고 잘 테스트된 코드를 우선한다. 항상 한 번에 한 개 테스트, 통과 후 구조 개선. 길지 않은 테스트를 매번 전체 실행.
Rust에서는 명령형보다는 함수형 스타일을 선호한다. Option, Result 콤비네이터(map, and_then, unwrap_or 등)를 if let이나 match 패턴 매칭 대신 가급적 사용한다.
프로젝트에 4주 가까이 썼다. 대부분 여행 중이거나 뇌진탕에서 회복 중에 했다. 당신들 중 어린 친구라면 훨씬 더 빨리 했을지 모르겠다. 참고로 시간 투입 내역은 다음과 같다:
시간당 커밋도 꽤 일정한 속도를 유지했다:
13시간 연속 코딩한 날도 있다. 이거 정말 중독성 있다!
또한, 이런 분석 역시 프로젝트가 어느 정도 완성되면 지니에게 시키면 얼마든지 해줄 수 있다.