We get asked this at the start of most real-time projects: live dashboards, trading and settlement systems, messaging, telemetry ingestion, multiplayer backends. Go or Rust?
The internet will hand you a benchmark. The benchmark is close to useless for your decision, because in the overwhelming majority of real systems neither language is your bottleneck. Your bottleneck is a database round trip, a third-party API, a serialisation format nobody revisited, or an N+1 query. Choosing Rust to save microseconds in a request that spends 40 milliseconds waiting on Postgres is optimising the wrong end of the problem.
So we decide on different grounds.
Where Go wins
Concurrency you can staff. A goroutine and a channel are learnable in a day. A competent engineer from almost any background is productive in Go inside a week and dangerous-in-a-good-way inside a month. On a system that needs to be maintained by whoever is on the team in two years, including people not yet hired, that matters more than most technical properties.
Compile times that keep you in flow. This sounds like a comfort issue and it is not. A build that takes seconds means you run the test suite constantly. A build that takes minutes means you batch changes, and batched changes are harder to debug when one of them is wrong.
The deployment story is boring. A single static binary, no runtime to install, trivial containers. On infrastructure where you may not fully control the host, boring is a feature.
Go is our default for network services, API gateways, ingestion pipelines, and anything where the work is fundamentally "wait on I/O, transform, forward".
Where Rust wins
When a pause is a defect. Go's garbage collector is very good and its pauses are sub-millisecond in most workloads. But it exists, and it runs when it decides to. If you are writing something where a stall has a hard cost (an order matching engine, an audio pipeline, a game server tick), Rust's lack of a collector is not a micro-optimisation, it is a categorical difference in what you can promise.
When memory is genuinely scarce. Embedded targets, edge devices, anything where the whole system has less RAM than a Go runtime would like to have as headroom.
When the concurrency is complex enough to be scary. This is the underrated one. Go makes concurrency easy to write; it does not make data races impossible. Rust makes an entire class of concurrency bug fail at compile time. If the design involves genuinely intricate shared state, "the compiler will not let me ship the race" is worth a great deal, arguably more than the performance.
The question we actually ask the client
Before either of the above, we ask: who is maintaining this in eighteen months, and what do they already know?
A theoretically superior Rust service, handed to a team of three PHP and Python engineers who now need to reason about lifetimes to fix a production bug at 2 a.m., is not the superior choice. It is a liability wearing the costume of good engineering. We have been called in to rescue exactly that situation more than once, and the rescue is always expensive.
Conversely, if a team already has Rust experience and the domain has hard latency requirements, choosing Go to be accommodating is its own kind of failure.
What we usually end up doing
On larger systems the answer is frequently both, and the seam is the interesting part. Go for the edges: HTTP, WebSockets, auth, orchestration, the parts that change often and touch the network. Rust for the one component with a hard real-time or CPU-bound requirement, kept deliberately small and behind a well-defined interface.
That split gives you the property that matters most: the code that changes weekly is in the language that is cheap to change, and the code that must never stall is in the language that guarantees it. The interface between them becomes the place you write your most careful tests, which is exactly where they belong.

