rust
GitHub migrates the Copilot runtime from TypeScript to Rust with agents and incremental validation
The rewrite of more than 800,000 lines shows how to decompose a difficult migration, preserve contracts, and use agents without replacing architectural oversight
GitHub has described an unusually ambitious migration: the agent runtime used by Copilot CLI, the Copilot app, and its SDK moved from TypeScript on Node.js and V8 to more than 800,000 lines of Rust. The work was completed primarily with coding agents across 128 pull requests that landed incrementally, while the rest of the team continued expanding the product.
The reason was not that TypeScript was a poor choice. For a terminal interface, it enabled fast development and delivered reasonable performance. The problem emerged when the same runtime became a shared component for products with different requirements around startup time, server density, memory use, and throughput. The initial architecture had also coupled the terminal interface and the runtime. The SDK eventually launched the CLI as a separate process and communicated through JSON-RPC, a practical solution that added processes, communication hops, and memory overhead.
The migration proceeded from the bottom up. First, the team established the Rust workspace, lint rules, continuous integration, build pipeline, and interoperability patterns. It then ported small pure-logic components without I/O or shared state, selected because they already had strong tests. Those pilots validated the full path: repository layout, FFI, packaging, testing, and review. Later work moved from utilities and filesystem operations to stateful subsystems, tools, model clients, MCP, and finally session orchestration, the most tightly coupled area.
Rust and TypeScript coexisted during the transition through N-API shims. The temporary surface peaked at 2,019 internal exports and 3,356 TypeScript call sites on August 3; by the end of the runtime migration, both internal counts were zero. The public interface remained: SDKs for TypeScript, Python, Go, C#, Java, and Rust still use a common contract, while Rust makes it possible to offer both a native Node addon and a C ABI entry point for clients that want to run the runtime inside their own process.
Validation mattered as much as code generation. Existing end-to-end tests were preserved, changes went through CI, and agents compared old and new behavior line by line. GitHub recorded 13,852 pnpm test runs, 8,437 cargo test runs, and thousands of lint and formatting checks during the effort. Human reviewers focused on architecture, contracts, risk, and ambiguous decisions. In one revealing case, automation tried to justify the accidental deletion of an SDK method with a waiver label; human review recognized that the change was unacceptable and the method was restored.
Known failures clustered around changed behavioral contracts, state or lifecycle differences, incomplete migrations, host and interop boundaries, and incorrect test oracles. Some came from subtle differences between JavaScript and Rust, including || versus unwrap_or, numeric types, and implicit time zones. The conclusion is not that agents make engineering unnecessary: they make a larger scale of work feasible, but still require explicit boundaries, tests that protect contracts, and a person able to judge whether the result is correct.

