Your laptop is the wrong place for rust-analyzer
Ten coding agents in ten worktrees means ten analyzers and ten cargo builds on one machine. We moved the analyzer, the builds and the tests to Linux nodes on the LAN, mirror the checkout there by content hash, and let the laptop do the one thing it is good at: editing.

On this page · 5 sections
The IDE on your laptop was designed for one human typing in one checkout. What runs on it now is a fleet: a resident agent in the main checkout, four workers in four worktrees, a research run reading everything, each of them asking rust-analyzer the same kind of question a human asks once a minute, and asking it ten times a second. Every worktree brings its own analyzer, which for a workspace of any size means gigabytes of resident memory and a startup that runs every build script and proc macro before the first hover answers. Every worker runs cargo test, and they all share the same sixteen cores with your editor. The fashionable answer is a bigger laptop. We think the laptop should only edit.
prod-code1 is what we run instead: a gateway process on each Linux node of a small cluster that holds the analyzer for a checkout in memory, keeps a mirror of the checkout next to it, and runs builds and tests in that mirror. The laptop keeps git and the editor. Agents talk to the gateway through an MCP server with 27 tools and never see a cold analyzer or a busy CPU on their own machine.
The analyzer lives in the gateway
For Rust the gateway does not spawn rust-analyzer; it links it. The ra_ap_ide crates are rust-analyzer as a library, and the gateway loads a Cargo workspace into a Salsa database inside its own process, so a hover is a function call on a warm database rather than a JSON-RPC round trip to a child. Go, C and C++, TypeScript, Python and Swift run as child language servers (gopls, clangd, the native TypeScript 7 server, basedpyright, sourcekit-lsp on the macOS nodes) behind the same wire protocol, so an agent does not know or care which kind it is talking to.
The database is per checkout and per git worktree, never shared between them; why that is the only defensible choice is its own post. The cost is a first load that runs the workspace’s build scripts and proc macros once, about 45 s for our own gateway repository, after which the engine stays resident and answers in single-digit milliseconds until it has been idle for thirty minutes. Against a warm workspace the bench drives 40,000 hovers per second at p50 1.4 ms and p99 3.5 ms over persistent sessions. An agent process keeps one session per checkout for its whole life, so a tool call is one request, not connect, sync, handshake and initialize: 20 hovers went from 2.8 s to 0.6 s when we made that change, about 10 ms each after the first.
The checkout is mirrored, not mounted
There is no network filesystem. The node holds a copy of the checkout, and the client’s job is to keep it current without sending the tree. First contact sends a manifest, path, size and FNV-1a hash per file; the gateway seeds a new worktree’s copy from the origin repository’s copy it already has, deletes what the client does not have, and asks only for what is missing. A fresh worktree of a 10 MB Rust repository is ready in 0.4 s instead of about 7 s. After that the client keeps a watermark per node: the commit it last synced and the dirty paths it sent, so the next sync is git diff since that commit plus the current dirty set, and a file reverted to its committed content is sent clean rather than left stale. File content travels as base64 inside the JSON frames, because a JSON byte array inflates content four-fold: a 10.8 MB workspace syncs in 0.76 s over 10 GbE instead of 8 s.
The cheap part is knowing when to sync. A one-shot CLI query costs about 80 ms, of which 70 ms are two git subprocesses on the laptop deciding what changed. The MCP server does not pay that: it watches the tree and runs the pre-flight sync only after something changed, right before the tool call that would otherwise see stale code.
Builds and tests run there too
$ prod-code exec -- cargo clippy --workspace --all-targets -- -D warnings
$ prod-code exec -- cargo test --workspace
The command runs inside the mirror on the node, in the workspace’s own target/, which stays warm between runs. Files the command creates or changes, formatter output, generated code, Cargo.lock, are written back into the checkout and recorded in the watermark, so cargo fmt on the node is indistinguishable from cargo fmt at home. On our own repository, with nothing compiled on the laptop:
| on a 32-core node | measured |
|---|---|
cargo clippy --workspace --all-targets |
2.5 s |
cargo test --workspace (64 tests) |
46 s |
| hover on a warm workspace | 1–4 ms |
| first load of a Rust workspace | ~45 s, once |
The same path carries code_check, code_lint and code_test with parsed diagnostics, and the tools that need the whole workspace in memory: references and call hierarchy, workspace symbol search, impact analysis from a diff to the tests it touches, dead-code scans, validation of a proposed edit before it is written.
Five nodes take this today, three Linux (two with 32 threads, one 128-core Ampere) and two Macs for Swift. Gateways gossip every 5 s, a client needs one seed address, and a checkout is placed on the node that already holds it, otherwise on the quietest one that serves its language.
$ prod-code cluster
⚡ prod-code cluster (5 node(s))
192.0.2.11:9400 UP load 0.10/cpu (32 cpus) uptime 0h25m workspaces 0 rss 10 MB
engines: rust, go, cpp, python, typescript
192.0.2.12:9400 UP load 0.01/cpu (32 cpus) uptime 0h24m workspaces 0 rss 13 MB
engines: rust, go, cpp, python, typescript
192.0.2.13:9400 UP load 0.01/cpu (128 cpus) uptime 0h25m workspaces 0 rss 12 MB
engines: rust, go, cpp, python, typescript
192.0.2.20:9400 UP load 2.96/cpu (24 cpus) uptime 1h06m workspaces 0 rss 14 MB
engines: swift
192.0.2.21:9400 UP load 0.36/cpu (11 cpus) uptime 1h06m workspaces 0 rss 9 MB
engines: swift
Workspace: prod-code
Engine needed: rust
The two macOS nodes advertise swift and nothing else, so placement cannot send a Rust
workspace to a workstation that merely happens to have cargo installed.
What it does not do yet
- The first load of a worktree is not shared with anything: about 45 s for a workspace whose build scripts and proc macros run at load, paid once per worktree, then about 2 s after an idle eviction.
- The laptop still runs git. The CLI’s one-shot queries spend most of their 80 ms in two
gitsubprocesses; only a long-lived agent process, through the MCP server’s watcher, avoids that. - An editor reaches the analyzer only through the LSP bridge (
prod-code lsp), where unsaved buffers arrive asdidChangeand are applied to the in-memory database as a session overlay. The MCP path agents use syncs from disk, so an agent never sees your unsaved edits. - Placement is by checkout, not by query. A large workspace lives on one node; nothing splits a single analysis across nodes.
The rule we ended up with: a laptop is for editing. Everything that needs the whole workspace in memory, or all the cores, runs where memory and cores are cheap and nobody is typing. If your agent setup’s next step is a bigger laptop, it is answering the wrong question.
Footnotes
-
github.com/alex09x/prod-code, Rust, MIT/Apache-2.0. The numbers in this post were measured on the cluster described in it; the wire protocol, the sync and the engines are in the repository. ↩
Cite this article
Alexander Panasenko (2026-09-21). Your laptop is the wrong place for rust-analyzer. https://prod.codes/blog/your-laptop-is-the-wrong-place-for-rust-analyzer/