GitHub는 GitHub Copilot CLI, Copilot 앱, Copilot SDK를 구동하는 런타임을 다시 구축했다. 기존 런타임은 TypeScript와 Node.js, V8 엔진에 의존했다. GitHub 블로그에 게시된 글에 따르면 그 결과 프로덕션용 Rust 코드가 800,000줄 이상 작성됐으며, 대부분은 인공지능 에이전트의 도움을 받아 128개의 풀 리퀘스트를 통해 메인 브랜치에 점진적으로 병합됐다.
작성자는 나머지 팀이 런타임 기능을 계속 개발하고 사용 범위를 확대하는 동안, 주로 한 명의 개발자가 참여해 작업이 몇 달 만에 완료됐다고 말한다. GitHub는 전환 이후 성능이 ‘몇 배나’ 향상됐다고 언급하지만, 해당 글에서 제공된 부분에는 이 개선을 측정할 구체적인 수치가 제시되지 않았다.
문제는 명령줄 인터페이스에만 있지 않았다
Copilot 런타임은 CLI 실행에만 사용되는 것이 아니다. GitHub Copilot CLI와 Copilot 앱, Copilot SDK를 비롯해 VS Code, Visual Studio, Cloud Agent, Copilot Code Review, Copilot Cowork, Copilot Studio, Excel·Outlook·PowerPoint·Word 앱 등 여러 버전과 제품에서 사용하는 공유 계층이다.
이전 설계에서는 런타임과 CLI 인터페이스가 상당히 긴밀하게 결합돼 있었다. 제품에 프로그래밍 방식의 접근이 필요해지자 SDK는 사실상 CLI 위에 구축됐고, 별도의 Node.js 프로세스를 실행한 뒤 JSON-RPC를 통해 통신했다. 이 방식은 빠르고 유연했지만, 각각의 소비 애플리케이션에 운영 비용을 부과했다.
새로운 CopilotClient를 생성할 때마다 Node.js와 V8을 호스팅하는 추가 프로세스를 실행하고, TypeScript에서 생성된 JavaScript 코드를 파싱하며, 엔진과 관련된 메모리 비용을 감당해야 했다. 또한 Node.js에서 장애가 발생하면 세션이 종료될 수 있었고, 애플리케이션은 최소 두 개의 프로세스를 모니터링해야 했다. 글에 따르면 C#, TypeScript, Python, Rust, Go, Java로 작성된 SDK 패키지들은 다른 용도로 Node.js가 필요하지 않은 경우에도 추가 런타임만으로 작업 세트 메모리에서 최소 약 100MB를 차지했다.
왜 Rust를 선택했나?
GitHub는 새로운 계층에 대해 명확한 목표를 설정했다. TUI에서 런타임을 분리하고, 의존성과 운영 비용을 줄이며, 동일한 프로세스 안에서 통합할 수 있도록 하고, 확장성과 신뢰성을 개선하는 것이었다. 또한 서로 다른 FFI 메커니즘을 통해 6개 SDK 버전에서 런타임을 사용할 수 있도록 C ABI가 필요했다.
글은 Rust가 이러한 요구 사항을 충족하는 데 도움을 줬으며, 최신 보안 모드를 지원하는 도구 체인, 공급망 위험 감소, 설계상 올바른 코드에 대한 더 큰 지원도 제공했다고 설명한다. 동시에 이 경험이 모든 대규모 TypeScript 프로젝트를 Rust로 전환하라는 권고는 아니라고 강조한다. 이 선택은 시작 시간, 메모리, 임베딩, 자원 사용량의 예측 가능성과 관련된 구체적인 요구 사항에서 비롯됐다.
전면 재작성 대신 점진적인 교체
GitHub는 장기간 유지되는 브랜치나 마지막에 한 번에 전환하는 방식으로 프로젝트를 진행하지 않았다. 대신 메인 브랜치 안에서 각 TypeScript 구성 요소를 하나씩 Rust 구성 요소로 교체하는 방식을 선택했다. 각 풀 리퀘스트는 Rust를 호출하는 얇은 연결 계층을 추가하고 동일한 변경에서 기존 구현을 삭제했다. 그 결과 브랜치는 배포 가능한 상태를 유지했고 새로운 코드는 시스템 안에서 즉시 테스트됐다.
- 프로젝트를 중단하지 않고 다른 개발자들의 일반적인 작업을 계속할 수 있었다.
- 각 변경 사항이 전면 재작성보다 작고 검토하기 쉬워졌다.
- CLI와 SDK의 엔드투엔드 테스트가 각 단계마다 실행됐다.
- 회귀 문제가 최종 전환 시점까지 미뤄지지 않고 마이그레이션 중에 발견되고 수정됐다.
GitHub는 각 구성 요소를 두 가지 버전으로 장기간 병렬 유지하는 방식도 거부했다. 저장소에서는 매주 수백 개의 풀 리퀘스트가 오갔고, 두 언어로 작성된 두 가지 구현과 두 세트의 의존성을 유지하면 복잡성이 커졌기 때문이다. 세션 형식 지정처럼 변경 가능한 상태를 관리하는 구성 요소에서는 두 버전을 비교하기가 더욱 어려워진다. 이러한 구성 요소는 콜백을 처리하고 시스템의 광범위한 부분과 연결되기 때문이다.
수치는 무엇을 보여 주나?
초기 추정치는 2026년 5월 약 130,000줄의 TypeScript에서 시작했지만 실제 작업 규모를 반영하지 못했다. TUI 계층에 포함된 것으로 계산됐던 구성 요소가 나중에 런타임으로 이전됐고, 마이그레이션과 동시에 새로운 TypeScript 코드도 계속 유입됐기 때문이다. 따라서 실제로는 약 430,000줄의 TypeScript가 이전 작업을 거쳤다.
같은 기간 프로젝트에는 약 300,000줄의 프로덕션 TypeScript가 추가되고 약 430,000줄이 제거됐다. 한편 약 1,200,000줄의 Rust가 추가되고 약 365,000줄이 제거됐다. 이 수치는 저장소에 나타나는 TypeScript 규모가 안정적으로 보였다고 해서 진전이 없었던 것은 아니라는 점을 보여 준다. 실제로는 추가와 삭제, 책임 재분배가 대규모로 진행되고 있었다.
왜 이 소식이 중요한가?
이 경험의 실질적인 가치는 Rust를 사용했다는 사실 자체가 아니라, 많은 제품이 공유하는 핵심 계층을 다시 작성한 방식에 있다. 각 구성 요소를 원자적으로 교체하고 브랜치를 배포 가능한 상태로 유지하면서 테스트를 지속적으로 실행하면 ‘대규모 전환’의 위험을 줄이고, 롤백하거나 문제 위치를 파악하기가 더 쉬워진다.
반면 이 글이 이러한 방식이 모든 조직에 적합하거나 인공지능 에이전트만으로 이 정도 규모의 재작성 품질을 보장할 수 있다는 점을 입증하는 것은 아니다. 또한 제공된 부분에는 전환 전후의 메모리 사용량에 대한 공개 측정값이 없으며, 검토·테스트·회귀 문제 처리에 들어간 인적 비용의 세부 내용도 설명되지 않는다. 따라서 가장 분명한 교훈은 Rust나 에이전트를 모든 마이그레이션의 일반적인 해결책으로 보는 것이 아니라, 점진적인 엔지니어링과 계층 분리에 관한 것이다.