Turborepo 팀이 빌드 도구를 Go에서 Rust로 완전 재작성하지 않고 점진적으로 포팅하는 전략을 택했다. Rust CLI 레이어가 기존 Go 코드를 감싸는 방식으로, 기존 동작을 유지하면서 신규 기능 개발을 병행한다. 첫 하이브리드 릴리스는 v1.7.0이다.
핵심 포인트- 완전 재작성 대신 점진적 포팅 선택 — 사용자 경험 저하 없이 기존 Go 코드베이스와 병행 개발이 가능하다.
- Rust가 Go를 JSON으로 호출하는 구조 — 두 언어 간 통신에 직렬화 레이어를 두어 타입 불일치를 단순화했다.
- Windows에서 MinGW/MSVC 충돌 — Rust 기본값(MSVC)을 MinGW로 전환하고 dunce 크레이트로 UNC 경로 문제를 해결했다.
- Alpine Linux(musl)에서 Go의 CGo가 세그폴트 — Rust와 Go를 완전히 분리된 바이너리로 컴파일해 JSON 통신으로 격리했다.
- 포팅 전 기존 동작을 정의하는 테스트 스위트 작성이 필수 — 이식 결과를 검증할 기준을 먼저 만들어야 한다.
상세 정리- 배경: Turborepo는 Go로 작성됐지만 팀은 Rust의 성능, 타입 안전성, 생태계를 위해 언어 전환을 결정했다.
- 전략 선택 이유: 완전 재작성은 신규 기능 중단, 기능 누락 위험, 미사용 코드 버그 증가라는 세 가지 단점이 있어 점진적 포팅을 택했다.
- 첫 번째 타깃 — 전역 turbo 명령: 사용자가 어디서나 실행할 수 있는 글로벌 CLI. Rust가 Go 바이너리를 호출하는 얇은 래퍼로 시작했다.
- CLI 파싱 이전: --cwd 같은 인수 파싱을 Rust로 옮기고, 결과를 Go에 JSON으로 넘기는 직렬화 레이어를 추가했다.
- Windows 문제 — 링킹 충돌: Go는 MinGW 기반인데 Rust 기본값이 MSVC라 링킹 충돌이 발생했다. Rust 컴파일 타깃을 x86_64-pc-windows-gnu(MinGW)로 강제 전환해 해결했다.
- Windows 문제 — UNC 경로: Go와 Rust가 Windows UNC 경로를 다르게 처리해 경로 불일치가 발생했다. Rust의 dunce 크레이트로 UNC 경로를 일반 경로로 정규화해 해결했다.
- Alpine Linux 문제: Go가 CGo를 통해 C 라이브러리에 의존하는데, Alpine은 glibc 대신 musl을 사용해 세그폴트가 발생했다. 정적 컴파일도 불가능했다.
- Alpine 해결책 — 바이너리 분리: Go와 Rust를 완전히 분리된 바이너리로 컴파일하고 JSON 통신으로 연결했다. CGo 의존성을 Rust 레이어에 전혀 노출시키지 않는 설계다.
- 테스트 전략: 포팅 대상 기능의 기존 동작을 정확히 정의하는 테스트를 먼저 작성해 이식 후 동작이 동일한지 검증했다.
- 직렬화의 가치: JSON FFI는 두 언어 간 데이터 교환을 단순화하고 플랫폼 간 호환성 문제를 상당 부분 제거했다.
- 첫 하이브리드 릴리스: v1.7.0 — Go 핵심 로직과 Rust CLI 레이어가 공존하는 상태로 배포되었다.
왜 읽나다중 언어 혼합 빌드 툴 개발이나 Go에서 Rust로의 마이그레이션을 고려하는 시스템/인프라 엔지니어에게 실전 크로스 플랫폼 함정과 설계 패턴 레퍼런스.