Bun 53만 줄 Rust 재작성: 64개 Claude Code 에이전트를 통제한 하네스

Bun의 53만 줄 Zig 코드를 11일 만에 Rust로 옮긴 사례를 바탕으로, 4개 워크트리와 독립 리뷰, 검증 루프가 64개 AI 에이전트를 통제한 방법을 정리합니다.

JavaScript 런타임 Bun의 주석 제외 53만 줄짜리 Zig 코드가 약 11일 만에 Rust로 옮겨졌습니다. 작업을 주도한 사람은 Bun 창립자 재러드 섬너였고, 피크 시점에는 최대 64개의 Claude Code 에이전트가 병렬로 움직였습니다.

숫자만 보면 “AI 에이전트를 많이 띄우면 대규모 개발도 순식간에 끝난다”는 이야기처럼 보입니다. 그런데 제가 더 중요하게 보는 장면은 따로 있습니다. 전체 포팅을 시작한 지 약 2분 만에 에이전트들이 같은 저장소를 건드리며 서로의 변경을 덮기 시작했다는 점입니다.

결론부터 말하면, 이 작업의 핵심은 64개 에이전트가 아니었습니다. 에이전트가 움직일 경계와 판정 기준을 만든 하네스, 실패를 다음 실행의 입력으로 바꾼 루프가 핵심이었습니다.

핵심 요약

  • Bun은 주석을 제외한 Zig 코드 535,496줄을 Rust로 기계적으로 포팅했습니다.
  • 약 50개의 Dynamic Workflow가 11일 동안 사용됐고, 피크 시점에는 4개 워크트리에서 각각 최대 16개 에이전트가 움직였습니다.
  • PORTING.md와 LIFETIMES.tsv가 모든 에이전트가 다시 읽을 공통 규칙과 예외 판단을 맡았습니다.
  • 1,448개 파일 전체를 맡기기 전에 대표 파일 3개로 구현, 리뷰, 수정 루프를 시험했습니다.
  • 구현자 1명마다 독립된 적대적 리뷰어 2명 이상을 붙이고, 구현 맥락과 리뷰 맥락을 분리했습니다.
  • 약 1만6천 개의 컴파일 오류를 실패 보고서가 아니라 다음 에이전트가 가져갈 작업 큐로 바꿨습니다.
  • 개인이나 작은 팀에는 64개 에이전트가 필요하지 않습니다. 구현자 1개와 독립 평가자 1개로도 같은 원리를 시작할 수 있습니다.

왜 Zig에서 Rust로 옮겼을까

Bun은 JavaScript와 TypeScript 런타임입니다. 패키지 매니저, 테스트 러너, 번들러, HTTP 서버처럼 다루는 범위가 넓고, JavaScript 엔진이 관리하는 메모리와 네이티브 코드가 직접 관리하는 메모리의 경계도 복잡합니다.

Bun의 공식 회고에 따르면 기존 Zig 코드에서는 use-after-free, double-free, 메모리 누수처럼 수명 관리와 관련된 문제가 반복됐습니다. Zig가 나쁜 언어여서가 아닙니다. Bun처럼 가비지 컬렉션 영역과 수동 메모리 관리 영역이 자주 만나는 프로젝트에서 사람이 모든 수명 규칙을 계속 지키는 일이 어려웠던 겁니다.

Rust를 선택한 이유도 여기에 있습니다. 안전한 Rust에서는 여러 메모리 실수가 컴파일 단계의 오류로 바뀝니다. 운영 중 충돌이나 퍼징 결과로 늦게 발견하던 문제를 더 이른 피드백으로 가져오려는 선택이었죠.

다만 새 아키텍처를 설계하는 전면 재개발은 피했습니다. 기존 Zig 구조와 동작을 최대한 유지하는 기계적인 포팅을 선택했고, TypeScript로 작성된 기존 테스트 스위트를 그대로 재사용했습니다. 런타임의 구현 언어와 테스트 언어가 분리돼 있었기 때문에 가능한 전략이었습니다.

시작 2분 만에 저장소가 꼬였다

대규모 포팅을 시작하자 에이전트들이 같은 Git 저장소에서 git stash, git stash pop, git reset 같은 명령을 실행했습니다. 한 에이전트가 임시 저장한 변경을 다른 에이전트가 꺼내고, 또 다른 에이전트가 상태를 되돌리면서 작업이 서로를 덮기 시작했습니다.

세 개의 에이전트 세션이 같은 Git 저장소에서 충돌하고 파일 단위 커밋과 위험 명령 금지로 대응한 구조

병렬성 자체보다 같은 작업 공간과 권한을 공유한 것이 먼저 문제였습니다. 이미지를 누르면 크게 볼 수 있습니다.

“에이전트마다 워크트리를 하나씩 주면 되지 않나?”라고 생각할 수 있습니다. 보통은 맞는 접근입니다. 다만 Bun 저장소는 크고, 각 작업은 나중에 함께 컴파일돼야 했습니다. 에이전트 수만큼 워크트리를 무한히 늘리면 디스크와 컴파일 비용이 감당되지 않았습니다.

재러드 섬너는 병렬도를 더 높이기 전에 워크플로의 권한과 쓰기 규칙을 먼저 바꿨습니다.

  • git stash, git reset 같은 위험한 Git 명령을 금지합니다.
  • 특정 파일을 한 번에 커밋하는 방식으로 변경 범위를 제한합니다.
  • 에이전트가 임의로 cargo나 느린 명령을 실행하지 못하게 합니다.
  • 전체 작업을 4개 샤드로 나누고, 샤드마다 별도 워크트리를 둡니다.
  • 각 워크트리에서 Dynamic Workflow 하나가 최대 16개 에이전트를 운영합니다.

이렇게 해서 나온 피크 숫자가 4개 워크트리 × 16개 에이전트 = 64개입니다. 중요한 순서를 기억해야 합니다. 64개를 먼저 띄운 것이 아니라, 충돌을 겪고 작업 경계와 권한을 고친 뒤 병렬도를 높였습니다.

작업 큐를 4개 워크트리로 나누고 각 Dynamic Workflow에서 최대 16개 에이전트를 운영한 구조

1인 1워크트리가 아니라 저장소 규모와 통합 빌드를 고려해 4개 샤드로 타협했습니다.

멀티 에이전트를 같은 저장소에서 운영한다면 Git Worktree로 작업 공간을 분리하는 방법도 함께 참고해보면 좋습니다.

긴 프롬프트 대신 판단 파일을 남겼다

긴 작업에서 모든 판단을 하나의 프롬프트에 넣으면 세션이 바뀔 때 맥락이 쉽게 손실됩니다. Bun은 두 개의 외부 메모리로 이 문제를 줄였습니다.

PORTING.md: 모든 에이전트가 따르는 공통 규칙

PORTING.md는 Zig 패턴과 타입을 Rust로 어떻게 옮길지 정리한 공통 규칙집입니다. 최소 동작 변경, 타입 매핑, 금지사항처럼 여러 에이전트가 같은 기준으로 판단해야 하는 내용을 파일로 고정했습니다.

LIFETIMES.tsv: 어려운 예외를 구조화한 결정표

LIFETIMES.tsv는 Rust로 표현하기 까다로운 구조체 필드의 수명과 소유권 판단을 기록했습니다. 각 필드를 분석한 뒤 두 명의 적대적 리뷰어가 판단을 검토하고, 피드백을 반영한 결과를 다음 에이전트가 다시 읽을 수 있게 직렬화했습니다.

Bun 공식 회고에 공개된 LIFETIMES TSV 생성 워크플로 프롬프트

구조체 필드의 수명 판단도 생성, 적대적 리뷰 2회 이상, 피드백 반영, 외부 파일 저장의 루프로 만들었습니다.

제가 말하고 싶은 건 특정 파일 확장자가 아닙니다. 장기 작업에서는 프롬프트를 길게 만드는 것보다 목표, 금지사항, 완료 조건, 반복해서 등장하는 예외 판단을 저장소 안의 검증 가능한 파일로 남기는 것이 더 중요합니다.

1,448개 파일 전에 3개로 시험했다

규칙 문서를 만들었다고 바로 1,448개 Zig 파일 전체를 맡기지는 않았습니다. 먼저 대표 파일 3개만 골라 작은 시험 운전을 했습니다.

각 파일에는 다음 역할이 붙었습니다.

  1. 구현자 1명이 Zig 파일을 Rust로 옮깁니다.
  2. 적대적 리뷰어 2명이 원본 동작과 규칙 준수 여부를 독립적으로 검토합니다.
  3. 수정 전담 에이전트가 리뷰 피드백을 반영합니다.

여기서 시험한 것은 Rust 코드만이 아닙니다. PORTING.md가 충분한지, 수명 판단이 일관적인지, 리뷰어가 실제 결함을 찾는지, 수정 결과를 다시 검증할 수 있는지까지 확인했습니다.

작은 팀에서도 이 순서를 그대로 가져올 수 있습니다. 인증 모듈 전체를 바꾸기 전에 엔드포인트 하나로 테스트와 리뷰 루프를 돌려보고, 프론트엔드 전체를 마이그레이션하기 전에 대표 컴포넌트 하나로 빌드와 시각 검증을 통과시켜보는 식입니다. 작은 실패는 규칙을 고치는 재료가 되지만, 큰 실패는 복구 작업이 됩니다.

구현자와 평가자의 컨텍스트를 분리했다

하나의 에이전트에게 코드를 작성하게 한 뒤 “네 코드를 다시 리뷰해줘”라고 요청하면 자기 검토 편향이 생기기 쉽습니다. 구현 과정에서 세운 가정과 설계 의도를 그대로 가진 채 결과를 보기 때문입니다.

Bun은 구현자 1명마다 적대적 리뷰어 2명 이상을 붙였습니다. 역할만 다르게 쓴 것이 아니라 컨텍스트 자체를 분리했습니다.

역할제공하는 맥락목표
구현자원본 Zig 코드, 포팅 계획, 공통 규칙동작을 유지한 Rust 코드 작성
적대적 리뷰어주로 diff와 완료 조건코드가 틀렸다고 가정하고 결함 탐색
수정자리뷰 결과와 대상 변경지적 사항 반영 후 다시 검증

리뷰어는 구현자가 왜 그런 판단을 했는지 설득부터 듣지 않습니다. 변경된 diff와 완료 조건을 보고, 작동하지 않을 이유를 찾습니다. 공식 회고에는 컴파일을 통과했지만 비동기 close 과정에서 use-after-free와 double-free를 만들 수 있었던 코드처럼, 별도 리뷰가 실제로 잡아낸 사례도 공개돼 있습니다.

AI가 생성하는 코드가 많아질수록 생성 속도보다 검증 속도가 병목이 됩니다. 별도 AI 코드 리뷰 레이어를 두는 방법도 같은 문제를 더 작은 팀의 관점에서 다룹니다.

1만6천 개 오류를 작업 큐로 바꿨다

전체 파일을 포팅한 직후에는 약 1만6천 개의 컴파일 오류가 남았습니다. 사람 한 명에게는 압도적인 숫자지만, 구조화하면 서로 독립적인 작업 큐가 될 수 있습니다.

워크플로는 crate별로 cargo check 결과를 파일에 저장하고, 오류를 파일 단위로 묶어 에이전트에게 나눴습니다. 한 에이전트가 수정하면 두 리뷰어가 검토하고, 수정자가 피드백을 반영한 뒤 커밋을 남겼습니다.

여기서 루프의 의미가 분명해집니다.

오류 수집
→ 작업 단위로 분류
→ 구현자에게 할당
→ 독립 리뷰
→ 수정
→ 같은 게이트로 재검증
→ 반복 실패 시 코드뿐 아니라 규칙도 변경

루프는 같은 명령을 무한 반복하는 기능이 아닙니다. 실패 원인이 다음 실행의 입력과 규칙을 바꾸는 경로입니다. 오류가 줄지 않거나 같은 우회 코드가 반복되면 개별 코드만 고치는 것이 아니라 워크플로의 규칙과 리뷰 기준을 함께 수정해야 합니다.

이 구조를 더 일반화한 내용은 Loop Engineering의 검증과 재시도 구조에서 이어서 볼 수 있습니다.

테스트 100% 통과는 끝이 아니다

Rust 이관 PR은 병합됐고, 여섯 개 플랫폼에서 기존 테스트가 모두 통과했습니다. Rust 기반 Bun은 Claude Code 일부 버전에도 실제로 적용되기 시작했습니다. 구현 이관 자체는 성공으로 볼 근거가 충분합니다.

Bun Rust 포팅의 성공을 구현 성공, 제품 성공, 일반 재현성의 세 축으로 나눈 판정

구현 성공이 제품의 장기 안정성과 작은 팀의 재현 가능성을 자동으로 증명하지는 않습니다.

하지만 제품 마이그레이션의 장기 성공은 별도 문제입니다. 2026년 7월 26일 기준 Bun의 최신 stable 릴리스는 v1.3.14이고, Rust 기반 v1.4는 아직 canary 단계입니다. 공식 회고도 병합 후 알려진 회귀 19개를 수정했다고 밝힙니다.

기존 테스트는 이미 예상한 동작을 확인합니다. 아직 모르는 보안 결함이나 메모리 문제까지 없다고 증명하지는 못합니다. Bun은 병합 뒤에도 여러 차례 보안 리뷰를 진행했고, JavaScript와 TypeScript를 포함한 파서에 지속적인 커버리지 기반 퍼징을 추가했습니다.

이 차이를 구분해야 합니다.

  • 검증 레이어는 테스트, 타입 검사, 독립 리뷰, 보안 검토, 퍼징처럼 서로 다른 실패를 찾는 장치를 겹치는 것입니다.
  • 검증 루프는 각 레이어에서 발견된 문제를 수정하고 같은 기준으로 다시 통과시키는 흐름입니다.

테스트를 통과했다는 사실과 장기 운영이 안전하다는 판단은 같은 말이 아닙니다. 언어가 안전성을 자동으로 완성해주는 것도 아닙니다. Rust의 컴파일러 피드백을 실제 제품 안전성으로 연결하려면 그 위에 리뷰, CI, 보안 검토, 퍼징을 계속 쌓아야 합니다.

커뮤니티는 어디에 주목했을까

관련 공개 영상 100개에서 순회 가능한 댓글과 답글 9,734개를 분류해보면, Zig·Rust와 당사자 간 논쟁을 언급한 반응이 약 34%로 가장 컸습니다. 안전성·버그·테스트는 약 10%, AI 과장과 마케팅 의심은 약 8%, 비용과 경제성은 5%였습니다.

반면 하네스, 워크플로, 독립 리뷰를 명시한 반응은 0.7%에 불과했습니다. 키워드 묶음은 중복을 허용하므로 비율을 합쳐 100%로 읽으면 안 됩니다. 이 수치는 언어 논쟁이 중요하지 않다는 뜻이 아니라, 실제로 재사용할 수 있는 운영 구조가 상대적으로 덜 이야기됐다는 뜻에 가깝습니다.

Bun Rust 재작성 관련 공개 반응 9734개에서 반복된 주제의 비율

사람들의 시선은 언어와 인물 갈등에 쏠렸지만, 작은 팀이 가져갈 수 있는 하네스 구조는 거의 언급되지 않았습니다.

API 정가 약 16만5천 달러를 어떻게 봐야 할까

이 작업에 사용된 토큰을 API 정가로 환산한 값은 약 16만5천 달러였습니다. 원화로 단순 환산하면 약 2.4억 원 규모입니다.

다만 이 숫자는 실제 청구서가 아니라 API 정가 환산액입니다. Bun이 Anthropic에 인수된 프로젝트이고 사전 공개 모델을 사용했다는 조건도 일반 개발팀과 다릅니다. “사람 3명이 1년 일할 비용보다 싸다”거나 “누구나 같은 비용으로 재현할 수 있다”고 단정하면 안 됩니다.

개인과 작은 팀이 복제할 대상은 비용과 병렬 규모가 아닙니다. 큰 변경을 작은 검증 단위로 바꾸고, 실패를 발견하면 다음 규칙을 고치는 운영 구조입니다.

개인과 작은 팀이 가져갈 5단계

개인과 작은 팀이 적용할 수 있는 입력 고정, 대표 작업 시험, 쓰기 영역 격리, 생성과 판정 분리, 실패 반영의 다섯 단계

64개 에이전트를 복제할 필요는 없습니다. 다섯 단계의 흐름을 작은 작업 하나에서 먼저 닫는 것이 출발점입니다.

1. 입력을 파일로 고정한다

목표, 금지사항, 완료 조건, 반복해서 등장하는 예외를 AGENTS.md, 기술 명세, 결정표 같은 파일로 남깁니다. 다음 에이전트가 이전 세션의 기억 없이도 같은 기준으로 판단할 수 있어야 합니다.

2. 대표 작업으로 루프를 시험한다

전체 마이그레이션 전에 파일 1개, 기능 1개, 엔드포인트 1개를 고릅니다. 구현, 테스트, 리뷰, 수정 흐름이 끝까지 닫히는지 먼저 확인합니다.

3. 쓰기 영역과 권한을 나눈다

가능하면 워크트리와 브랜치로 작업 공간을 격리합니다. 같은 저장소를 공유해야 한다면 파일 소유권과 허용 명령을 더 엄격하게 제한합니다. 병렬도는 격리 구조가 검증된 뒤에 높입니다.

4. 구현자와 평가자를 분리한다

구현자 에이전트 1개와 평가자 에이전트 1개부터 시작해도 충분합니다. 평가자에게는 구현자의 긴 추론 대신 diff와 완료 조건을 전달하고, 문제가 있다고 가정한 상태에서 검토하게 합니다.

5. 실패를 다음 실행의 입력으로 바꾼다

테스트 실패와 컴파일 오류를 보고서로만 남기지 않습니다. 다음 작업 큐로 만들고, 같은 실패가 반복되면 코드뿐 아니라 규칙과 검증기도 함께 수정합니다.

64개보다 중요한 숫자

64개의 AI 에이전트는 눈길을 끕니다. 53만 줄과 11일, 약 16만5천 달러도 마찬가지입니다. 하지만 이 숫자만 가져오면 대규모 병렬 실행을 흉내 내다가 충돌과 비용만 키우기 쉽습니다.

제가 이 사례에서 가져가고 싶은 숫자는 구현자 1명과 독립 평가자 1명입니다. 이 최소 단위만 있어도 생성과 검증의 컨텍스트를 분리할 수 있고, 테스트 실패를 다음 작업으로 연결하는 작은 루프를 만들 수 있습니다.

에이전트가 강해질수록 사람의 역할이 사라지는 것이 아니라 달라집니다. 코드를 한 줄씩 작성하는 시간보다 목표와 경계, 판정 기준, 실패 이후의 경로를 설계하는 시간이 더 중요해집니다. 하네스는 에이전트가 움직일 경계와 판정 기준이고, 루프는 실패가 다음 실행의 입력으로 바뀌는 경로입니다.

관련 자료

자주 묻는 질문

작업 초반에 에이전트들이 충돌한 이유는 무엇인가요?

같은 저장소에서 여러 에이전트가 git stash, git reset 같은 명령을 실행하며 서로의 변경을 덮었기 때문입니다. 위험한 Git 명령을 금지하고 파일 단위 커밋과 4개 샤드 워크트리로 경계를 나눈 뒤 병렬도를 높였습니다.

구현자와 리뷰어의 컨텍스트를 왜 분리했나요?

같은 에이전트가 자기 코드를 리뷰하면 자기 검토 편향이 생기기 쉽기 때문입니다. 리뷰어는 주로 diff와 완료 조건만 보고 코드가 틀렸다고 가정한 상태에서 결함을 찾았습니다.

테스트가 모두 통과했으니 Rust 전환은 완전히 성공한 건가요?

구현 이관은 성공으로 볼 근거가 충분하지만 제품의 장기 안정성은 별도 문제입니다. 병합 후 알려진 회귀 19개를 수정했고, 보안 리뷰와 커버리지 기반 퍼징을 계속 추가했습니다.

개인이나 작은 팀은 이 사례에서 무엇을 가져가면 되나요?

64개 에이전트가 아니라 구현자 1개와 독립 평가자 1개로 시작하면 됩니다. 입력을 파일로 고정하고, 대표 작업으로 루프를 시험하고, 실패를 다음 실행의 입력으로 바꾸는 흐름이 핵심입니다.

이 글이 도움이 됐나요?

좋았다면 링크를 공유하거나 반응을 남겨주세요. 남겨주신 반응은 다음 글을 개선하는 데 참고하겠습니다.