Rewriting Blue's Backend in Rust, One Query at a Time
I am replacing Blue's Node backend with Rust while 19,000+ companies keep using it. Why now, why Rust, and the first results from production.

Yesterday morning, Blue’s backend was 100% Node.js. Today, twenty of its capabilities run on Rust. About 360,000 requests went through the new code in the last twelve hours, and nobody noticed anything except that things got faster.
That is the whole point. I am replacing Blue’s backend piece by piece, while 19,000+ companies keep working in it.
Why a rewrite makes sense now
Blue’s backend has been in production since 2018. It has seven years of accumulated decisions, and it has hit the ceiling of what Node can do. Building a large workspace’s records takes seconds, and the memory bill grows with every record.
The standard advice is “never rewrite”. It was right when a rewrite cost a full team several years. It is wrong now. Agents write the code, so typing is no longer the cost. What is left is proving that the new code behaves exactly like the old code, and proof is something I can build.
I have done a big bang before
When I rebuilt the data model for databases, I did it as one cutover, because the old model and the new one could not run side by side. Over the following week I fixed more than two hundred issues.
I got through it, and I will not do it again. A big bang puts every risk into one weekend. For a backend that 19,000+ companies depend on, I want many small changes, each one proven and each one reversible.
The strangler fig
The approach has a name. Microsoft’s architecture guide on the strangler fig pattern describes it: put a front door in front of the old system, move one capability at a time to the new system, and when nothing depends on the old one, switch it off. It is named after the fig that grows around a tree until the tree is gone.

For Blue, Node is the front door. The app, the API and every integration keep talking to the same place. When a capability moves, Node asks the Rust service for the answer and passes it on. Both backends run in production at the same time, on the same database.
Three rules keep it safe:
- Merging is deploying. There are no flags and no percentage rollouts. One code path, for everyone.
- Rollback is one
git revert. - If Rust fails, Node answers. In the last twelve hours, fewer than one call in 500 fell back to Node, and no customer saw an error.
Why Rust and not Go
This was a real decision. Blue’s CLI and Cobalt are both written in Go. I know Go, I like it, and it compiles faster. I ran a pilot to find out: the same feature, the same AI model, two separate agents, one in each language. Both passed every test.
Rust won on two things I care about for the next ten years.
Correctness. The Go version made a mistake that Rust refuses to compile. It read a column that can be empty as if it could never be empty, and every create stopped delivering webhooks. In Rust, a whole family of these mistakes is impossible to write. Since I do not read the code line by line, I want the compiler to be the strictest reviewer in the room.
Performance. Both languages are far faster than Node. At the largest scale I tested, a 50,000-record workspace, Node needed 131 seconds. Go needed 9.2 seconds and Rust needed 5.7 seconds. Rust used 1.9 GB of memory, against 3.8 GB for Node and 4.5 GB for Go.
I accepted a slightly slower build loop in exchange for both.
Two reviewers that never get tired
Agents write the Rust, so review has to come from somewhere else.
The first reviewer is the compiler. Whatever it rejects, no person ever needs to find.
The second is a black-box test harness, a separate program that knows nothing about either backend. It only talks to Blue the way a customer’s browser or integration would, and it runs the same scenarios against Node, against Rust and against both together. When they disagree, I do not bend the test. I prove who is right, and the test stays as it is.
Every migrated query passes both before it ships.
The results
In isolated benchmarks with byte-identical output:
| Workload | Node | Rust |
|---|---|---|
| Build 5,000 records × 50 fields | 1,399 ms | 135 ms |
| Build 50,000 records × 50 fields | 131 s | 5.7 s |
| Evaluate 10,000 formulas | 68 ms | 14 ms |
In production, the work behind each migrated query now takes a few milliseconds:
| Query | Node, p95 | Rust, p95 |
|---|---|---|
userChanges | 159 ms | 3 ms |
organizationChanges | 170 ms | 5 ms |
workspaceChanges | 179 ms | 5 ms |
hasUnreadInboxThreads | 156 ms | 5 ms |
recordDatabaseMenuOrder | 162 ms | 5 ms |
githubRepoLink | 127 ms | 4 ms |
The heavier queries gain even more in absolute terms. automationList went from a 668 ms median to 55 ms. savedViews dropped from 262 ms to 116 ms at p95.
Twenty capabilities moved in about a day, each with its own tests, its own review and its own rollback.
What happens next
Every part of the backend will move to Rust, one piece at a time, until Rust covers 100% of it. Then the Node code is deleted and the fig has finished its job.
The only thing you will notice is that Blue gets faster and more consistent.