Ladybird 브라우저, AI 도움으로 Rust 전환 시작
Ladybird adopts Rust, with help from AI
TL;DR Highlight
독립 브라우저 엔진 Ladybird가 C++에서 Rust로 전환을 시작했는데, Claude Code와 Codex를 활용해 JavaScript 엔진 2.5만 줄을 2주 만에 포팅하고 회귀 버그 0건을 달성한 사례.
Who Should Read
대규모 C++ 코드베이스를 메모리 안전 언어로 마이그레이션하려는 시스템 개발자, 또는 AI 코딩 도구를 활용한 언어 포팅에 관심 있는 개발자.
Core Mechanics
- Ladybird는 이전에 Swift를 시도했지만 C++ interop이 부족하고 Apple 외 플랫폼 지원이 제한적이어서 포기했고, 결국 생태계가 더 성숙한 Rust를 선택했다.
- 2024년에는 Rust가 C++ 스타일 OOP(깊은 상속 계층, GC 등)에 안 맞다고 거절했는데, 1년 더 고민한 끝에 실용적 판단으로 Rust를 채택했다. Firefox와 Chromium도 이미 Rust를 도입 중이라는 점도 영향을 줬다.
- 첫 포팅 대상은 LibJS(JavaScript 엔진)의 렉서, 파서, AST, 바이트코드 생성기로, test262라는 광범위한 테스트 스위트가 있어 검증이 수월한 부분부터 시작했다.
- Claude Code와 Codex를 사용했지만 자율 코드 생성이 아니라, 사람이 무엇을 어떤 순서로 어떤 형태로 포팅할지 결정하고 수백 개의 작은 프롬프트로 방향을 잡는 방식이었다. 번역 후에는 다른 모델들에게 adversarial review를 돌려 실수와 나쁜 패턴을 잡았다.
- 핵심 요구사항은 C++과 Rust 파이프라인의 출력이 바이트 단위로 완전히 동일한 것이었다. 약 2.5만 줄의 Rust 코드를 2주 만에 완성했고, 수작업이었으면 수개월 걸렸을 작업이다.
- test262 52,898개, Ladybird 회귀 테스트 12,461개 전부 통과하고 회귀 0건. JS 벤치마크에서도 성능 저하 없음. 추가로 C++과 Rust 파이프라인을 동시 실행하는 lockstep 모드로 실제 웹 브라우징까지 검증했다.
- 현재 코드는 'C++에서 번역된' 느낌이 강하고 idiomatic Rust는 아니지만, 의도적으로 C++ 레지스터 할당 패턴까지 그대로 따라해서 바이트코드 동일성을 확보한 것이다. 정리는 C++ 파이프라인 퇴역 후에 할 예정.
- Rust 전환이 프로젝트의 메인 포커스가 되는 건 아니고, C++로 계속 개발하면서 서브시스템별로 천천히 포팅해나갈 계획이다. 코어 팀이 관리하므로 포팅 기여는 사전 조율 필요.
Evidence
- 바이트 단위 동일 출력 요구사항이 이 포팅의 가장 영리한 부분이라는 의견이 많았다. 보통 리라이트가 실패하는 이유가 포팅하면서 '개선'까지 하려다 원래 코드 버그인지 새 코드 버그인지 구분 못하기 때문인데, 이 방식은 diff로 바로 잡을 수 있다는 점이 좋다는 것.
- AI 코딩 도구로 리뷰를 대신하는 것에 대한 우려도 있었다. '모델에게 실수를 찾아달라는 건 결국 본인이 리뷰할 수 없거나 하기 싫다는 뜻'이라며, 명확한 패턴은 잡지만 깊은 논리 오류는 못 찾는다는 반론이 제기됐다.
- C++로 계속 개발하면서 Rust 포팅을 사이드로 진행하겠다는 계획에 대해, 결국 이중 작업이 되는 것 아니냐며 새 코드는 전부 Rust로 작성하는 게 낫지 않냐는 의견도 있었다.
- Andreas Kling의 기업가적 역량에 주목하는 시각도 있었다. Jakt → Swift → Rust로 언어를 계속 바꾸는 게 엔지니어링보다 업계 관심과 후원을 끌기 위한 전략적 판단일 수 있다는 분석이다.
- AI가 보편화되면서 '다른 언어로 전면 재작성'의 비용 계산이 완전히 달라졌다는 의견이 있었다. 특히 광범위한 테스트 스위트가 있는 경우 AI 포팅이 매우 효과적이고, 테스트의 중요성이 10배로 커졌다는 것.
How to Apply
- C++/Java 등 레거시 코드를 다른 언어로 포팅할 때, AI 도구에 자율적으로 맡기지 말고 '무엇을, 어떤 순서로, 어떤 형태로' 변환할지 사람이 결정한 뒤 작은 단위의 프롬프트로 나눠서 진행하면 품질을 유지하면서 속도를 크게 높일 수 있다.
- 언어 마이그레이션 시 기존과 새 파이프라인의 출력을 바이트 단위로 비교하는 lockstep 검증 방식을 도입하면, 포팅 과정에서 생긴 버그를 즉시 잡을 수 있다. 이를 위해 첫 단계에서는 idiomatic한 코드보다 기존 코드와의 동작 일치를 우선할 것.
- AI 번역 후 단일 모델에만 의존하지 말고, 다른 모델을 사용한 adversarial review(적대적 검토)를 여러 패스 돌려서 패턴 오류와 실수를 잡는 다중 검증 루프를 적용할 수 있다.
- 포팅 대상을 고를 때 test262처럼 광범위한 테스트 커버리지가 있는 자족적(self-contained) 모듈부터 시작하면, 정확성을 검증하면서 안전하게 진행할 수 있다.
Terminology
test262ECMAScript(JavaScript) 표준 준수를 확인하는 공식 테스트 모음. 5만 개 이상의 테스트 케이스로 JS 엔진이 스펙대로 동작하는지 검증한다.
lockstep mode두 시스템을 동시에 실행하면서 매 단계마다 출력이 동일한지 비교하는 검증 방식. 한쪽이 다르게 동작하면 바로 잡힌다.
ASTAbstract Syntax Tree. 소스 코드를 트리 구조로 변환한 것으로, 코드의 문법적 구조를 표현한다. 컴파일러/인터프리터의 핵심 중간 표현.
adversarial review일부러 적대적인 관점에서 코드를 분석하는 리뷰 방식. 여기서는 여러 AI 모델에게 '이 코드에서 실수와 나쁜 패턴을 찾아라'고 요청하는 것을 의미.
C++ interopC++ 코드와 다른 언어 코드가 서로 함수를 호출하고 데이터를 주고받을 수 있게 하는 상호운용성. Swift의 C++ interop이 미성숙해서 Ladybird가 포기한 이유 중 하나.