GitHub published a detailed engineering account on September 16, 2026 of rewriting the runtime behind Copilot CLI, the Copilot app, and the Copilot SDK from TypeScript/Node.js to Rust — a migration that produced 832,378 lines of production Rust plus 468,689 lines of unit tests, built primarily by one engineer working with Copilot itself, across 128 merged pull requests over roughly 14.5 weeks. Copilot's own agents handled 61% of the 1.13 million tool calls the project required. It's one of the most concretely documented large-scale AI-agent-driven codebase migrations published by a major engineering org to date — real numbers on cost, cache hit rates, regressions, and what actually broke, not just a headline claim.
TL;DR
| Question | Answer |
|---|---|
| What was migrated? | Copilot CLI/app/SDK's shared runtime, from TypeScript/Node.js to Rust |
| Final size | 832,378 lines of production Rust + 468,689 lines of unit tests |
| Timeline | ~14.5 weeks (May 12 – August 21, 2026), shipped incrementally across 135 releases |
| Team size | One primary human engineer, agent-assisted throughout |
| AI's share of the work | 61% of ~1.13M total tool calls came from Copilot-spawned subagents |
| What broke | Dozens of regressions — type ambiguities, timezone/env handling, lifecycle bugs — all fixed by Sept 14 |
| Unsafe code | 158 unsafe blocks across 36 files, concentrated at FFI/OS/SQLite boundaries, zero known regressions traced to them |
Why rewrite the runtime at all
The architectural motivation is concrete, not aesthetic. Copilot's runtime originally ran as TypeScript on Node.js and the V8 JavaScript engine, exposed to SDK clients only through a JSON-RPC process boundary. That meant every SDK client had to spawn a separate Node.js process, load the full JavaScript runtime, and communicate over JSON-RPC — adding roughly 100MB of working-set memory per client, real process-management complexity, and a hard ceiling on startup speed, throughput, and how many clients a single server could support. For a CLI tool that's fine; for an SDK meant to be embedded into other applications at scale, that overhead compounds fast.
The rewrite's new architecture exposes two access paths instead of forcing everyone through the same process boundary: a C ABI "door" for in-process embedding via FFI — 19 exported C functions dispatching to 364 internal routes — and a JSON-RPC server retained for the out-of-process scenarios where that's still the right shape. GitHub reports the process-hop elimination and V8/Node startup removal delivered what it describes as an "orders of magnitude" performance improvement, though the specific benchmark numbers weren't broken out in the published account.
The scale of what one engineer actually shipped
The headline number worth sitting with: one primary engineer, agent-assisted, led a migration that GitHub's own retrospective frames directly — "a project that would have taken a whole team of developers a year or two before agents was now completed primarily by a single developer, in only a few months." That's not a vague productivity claim; it's backed by session-level telemetry GitHub published alongside it: 12,760,995 events processed, 1,857,409 tool invocations, 1,385,214 assistant messages, and just 31,247 human messages across the entire project.
The human's role, per the message breakdown, was substantially supervisory rather than implementational — of 2,639 primary human messages, roughly 31% were review, testing, or CI-related, 17.4% addressed technical challenges the agent surfaced, and 15% were completeness checks. Prompt caching did real work here too: a 96.22% cache hit rate across the project, with only 0.71% of tokens being genuinely fresh input — the kind of efficiency that makes a months-long, continuously-running agentic project economically viable rather than prohibitively expensive to keep context-loaded on.
The dependency and safety picture
Migrating off Node.js meant removing roughly 60 npm packages that existed purely to support the runtime — some were clean one-to-one swaps (js-tiktoken → tiktoken-rs, minimatch → globset), others required consolidating multiple packages into fewer, more capable Rust crates (eight separate opentelemetry/* packages collapsed into four crates plus a custom implementation), and five packages were reimplemented from scratch in Rust because no adequate equivalent existed.
On safety specifically, the migration introduced 158 unsafe blocks across 36 files — concentrated almost entirely at the boundaries where Rust has to interoperate with something outside its own memory model: the C ABI boundary (32.3%), Windows API calls (31.0%), POSIX/libc calls (29.1%), and SQLite bindings (4.4%). GitHub's specific claim is that zero known regressions traced back to unsafe code — the bugs that did surface came from ordinary porting mistakes, not memory-safety violations, which is a meaningfully different risk profile than "we wrote a lot of unsafe Rust and hoped for the best."
What actually broke, and how they found it
GitHub is unusually candid about failure modes, which is what makes this account more useful than a typical launch post. Dozens of regressions surfaced during the migration, all fixed by September 14, 2026, and the report categorizes the recurring patterns directly: incomplete migrations (a feature ported partially, not fully), state and lifetime issues (Rust's ownership model surfacing bugs TypeScript's garbage collection had silently papered over), behavioral contract mismatches, host-boundary problems, and incorrect test oracles (tests that were themselves wrong, not the code they tested). The specific examples given are mundane in exactly the way that makes them credible: type ambiguities like 42.0 versus 42 slipping through, or ambient behaviors like timezone handling and environment-variable defaults that didn't carry over identically between the two languages — as the report puts it, "every one of these is ordinary wiring: a name output slightly wrong, a signature that didn't line up." GitHub is explicit that more regressions likely exist, undetected, in production — an honest caveat rather than a victory-lap omission.
Shipping incrementally, not in one cutover
One detail in the report worth calling out on its own, because it's the operational choice that likely made the whole approach survivable: GitHub didn't sit on the migration for 14.5 weeks and then flip a switch. It shipped 135 releases during the port — 100 pre-release and 35 stable, averaging 1.3 releases per day — meaning the Rust runtime was going out the door continuously alongside the existing TypeScript system, not held back for one big-bang cutover at the end. That cadence is what let regressions get caught and fixed in near-real-time rather than accumulating invisibly across four months and surfacing all at once at launch.
It also explains why the human engineer's message breakdown skews so heavily toward review and testing (31%) rather than writing code from scratch: with releases going out more than daily, the actual bottleneck wasn't generating Rust — Copilot's subagents handled 61% of the 1.13 million tool calls involved — it was verifying that each incremental release hadn't quietly broken something a downstream client depended on. The interop surface between the two languages peaked at 2,019 exports and 3,356 call sites mid-port, which gives a sense of how much surface area needed continuous checking while both implementations coexisted.
Why the dependency consolidation matters beyond line count
The ~60 npm packages removed weren't all equivalent swaps, and the split is informative about what a language migration actually costs versus what it saves. Straightforward one-to-one replacements (js-tiktoken → tiktoken-rs, the ignore crate, minimatch → globset) are close to free — find the Rust equivalent, swap the import, move on. The harder cases were consolidations, like collapsing eight separate opentelemetry/* npm packages into four Rust crates plus a custom implementation, which requires actually understanding what each of the eight packages was doing and re-deriving that behavior in a smaller, differently-shaped set of dependencies rather than mechanically translating package-for-package. Five packages had no adequate Rust equivalent at all and were reimplemented from scratch — the kind of decision that's easy to make badly (reimplementing more than necessary, or missing edge cases the original package handled) and is exactly where sustained human review time, not raw code generation speed, does the real work.
Honest limitations
- This is GitHub's own published account of its own project — a detailed, metrics-rich retrospective, but not an independently audited case study.
- GitHub explicitly states more regressions likely exist undetected in production beyond the "dozens" already found and fixed.
- The in-process embedding path is still opt-in while confidence builds, and the CLI itself hasn't been fully re-layered onto the public SDK yet — the migration is functionally complete but not fully consolidated architecturally.
- "Orders of magnitude" performance improvement is stated without the specific before/after benchmark numbers in the published account — a real claim, but less precisely quantified than the line-count and token-cost figures.
What this means for builders
This is one of the more concrete, numbers-backed data points available for the question every engineering team is quietly asking: how far can one person go on a large, unglamorous migration with heavy agent assistance? GitHub's own answer — a year-or-two, whole-team project compressed to a few months for one supervising engineer, with real regressions caught and fixed rather than hidden — is a useful calibration point specifically because it doesn't oversell: the published account leads with what broke and why, not just the line count. If you're scoping a large migration or rewrite of your own, the message-category breakdown here (most human time spent on review, testing, and CI, not writing code) is a more actionable planning input than the "832K lines" headline alone.
Related on explainx.ai
- Turso: SQLite rewritten in Rust, a complete guide
- Bun's Zig-to-Rust AI rewrite, explained
- Claude Code's Bun 1.4 Rust runtime
- GigaToken: a 1000x faster Rust tokenizer
- Claude Code subagents and multi-agent workflows
- Should developers stop reviewing AI-generated code?
- Official source: GitHub Blog — Migrating the GitHub Copilot runtime to Rust, using Copilot
This post is sourced to GitHub's own September 16, 2026 engineering blog post, authored by Stephen Toub. All figures — line counts, timeline, tool-call breakdowns, and regression counts — are GitHub's self-reported project telemetry.
