Skip to main content
Engineering

Rust vs Go for your backend: how we choose

9 min read

Both are modern, fast and compiled. The useful question is which one fits the problem in front of you. Here's how we decide.

We build production systems in both Rust and Go, and our own product runs on both: Go where iteration speed matters, Rust where latency and memory safety are non-negotiable. So when someone asks which language they should use for a new backend, our honest answer is: it depends, and anyone who gives you a one-word answer is selling something.

This post is the comparison we wish more people made. It skips the benchmark shoot-out and looks at the practical trade-offs that decide the choice on real projects.

What they have in common

Start with the agreement, because it's large. Both compile to native code, both are fast enough that the language is rarely your bottleneck, both ship a single static binary that's trivial to deploy in a container, and both have first-class concurrency and a healthy ecosystem. Either one is a defensible choice for a backend service. You will not regret picking the 'wrong' one nearly as much as internet arguments suggest.

Where Go wins

Go optimises for one thing above all: getting a team productive and keeping it that way. The language is small enough to learn in a week, the standard library covers most of what a service needs, and goroutines make concurrent code approachable rather than terrifying. Compilation is near-instant, which keeps the feedback loop tight.

  • Standard backend services: REST and gRPC APIs, microservices, orchestration services, workers.
  • Teams that need to move fast and onboard new engineers quickly.
  • High-concurrency network services, where goroutines shine and the runtime does the hard part.
  • Anything where simplicity and readability matter more than squeezing out the last few percent of performance.

The cost is that Go is garbage-collected. For the overwhelming majority of services that's a non-issue, because the collector is excellent. But it means occasional pauses and less control over memory layout, which matters for a specific class of latency-sensitive work.

Where Rust wins

Rust gives you C-class performance with memory and thread safety enforced at compile time, and no garbage collector. The borrow checker that frustrates newcomers is the same mechanism that makes whole categories of bug (use-after-free, data races, null dereferences) impossible to ship. When correctness and speed both matter, that guarantee is worth the steeper learning curve.

  • Systems and networking software: protocols, proxies, agents, the data plane.
  • Real-time pipelines where a garbage-collector pause is unacceptable, like live media.
  • Performance-critical components inside a larger system, often via FFI or as a focused service.
  • WebAssembly, where Rust is a first-class citizen for shipping native-speed code to the browser.

The cost is real: Rust takes longer to learn, longer to write, and longer to compile. A team new to it will be slower for the first few months. That investment pays off in the right domain and is pure overhead in the wrong one.

How we actually choose

On our own platform the split fell out naturally: Go where iteration speed and readability matter most, Rust where latency and memory safety are non-negotiable. We didn't pick a side; we matched the tool to the job.

Reach for Rust when you cannot accept a crash, a data race or a collector pause. Reach for Go for almost everything else. Most backends are 'everything else'.

A useful default: start a new backend in Go unless you have a concrete reason not to, and introduce Rust surgically for the components that truly need it. You don't have to choose one language for the whole system. A Go service with a Rust component behind FFI, or a small Rust service behind a Go API, is a perfectly sensible architecture.

If you're weighing this for a real project and want a second opinion grounded in shipping both, that's exactly the kind of conversation we're happy to have.