Jump to content

Rust Eats Fewer Cores. Go Eats Fewer Weekends

From JOHNWICK
Revision as of 08:42, 19 November 2025 by PC (talk | contribs) (Created page with "500px Your cloud bill does not care about your feelings.
Your pager does. Rust keeps the bill small.
Go keeps the pager quiet. That is the real trade. Not syntax. Not memory model. Not hype. You either spend money on CPU…
Or you spend your Saturday on incident calls. Pick. The fight is not Rust vs Go. It is you vs 3 A.M. Let me give you a real picture. We had an internal service doing ~22k requests per second at burst. Heavy JS...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Your cloud bill does not care about your feelings.
Your pager does. Rust keeps the bill small.
Go keeps the pager quiet. That is the real trade. Not syntax. Not memory model. Not hype. You either spend money on CPU…
Or you spend your Saturday on incident calls. Pick. The fight is not Rust vs Go. It is you vs 3 A.M. Let me give you a real picture. We had an internal service doing ~22k requests per second at burst. Heavy JSON in, light result out. Mostly CPU and a little IO. Classic “stateless but hot” tier. Go version:

  • Easy to write.
  • Easy to onboard new people.
  • Panic, observe stacktrace, patch, redeploy.

Rust version:

  • Annoying to write at first.
  • Review cycles are longer.
  • Type system argues with you for hours.

Now the numbers. Go (GOMAXPROCS=8):

 steady load: ~68% CPU
 burst load: ~96% CPU
 p95 latency under burst: ~320 ms

Rust (tokio, same hardware):

 steady load: ~41% CPU
 burst load: ~59% CPU
 p95 latency under burst: ~180 ms

Same hardware.
Same request shape.
Same feature. Rust left headroom. Go did not. That headroom is the difference between “scale this box” and “wake this human.” This is why infra people love Rust. Fewer cores means fewer nodes, fewer autoscale events, fewer noisy alerts. But here is the trap nobody tells you: that Rust chart hides pain that already happened. Because while Rust was shaving milliseconds, Go was shipping features. So now we ask the only question that matters. Are you optimizing for cost, or are you optimizing for recovery? Go buys you recovery time Go is gentle to humans. You can jump into a Go codebase someone else wrote last quarter and not feel like you opened a quantum physics textbook during turbulence. When a bug hits production in Go, the loop is usually boring:

  • Add a log.
  • Reproduce on staging.
  • Push a fix.
  • Go back to lunch.

When a bug hits production in Rust, sometimes the loop is:

  • Wait, how did this even compile?
  • Why is this lifetime fighting me now, not earlier?
  • Which one of these traits is actually in use at runtime?

You do not always get “quick patch, redeploy, breathe.” You get “I need the original author on call.” This is not a language problem. This is an organizational problem. Go scales teams.
Rust scales machines. That is the core split. The real bill: cloud vs people Let me draw it like we showed it to leadership: +----------------------+----------------------+----------------------+ | | Go Service | Rust Service | +----------------------+----------------------+----------------------+ | CPU at burst (8 vCPU)| ~96% | ~59% | | p95 latency burst | ~320 ms | ~180 ms | | Dev ramp-up days | 2-3 | 7-10 | | On-call comfort | High | Medium | | Infra cost/unit | Higher | Lower | +----------------------+----------------------+----------------------+ You see it. Rust is cheaper to run.
Go is cheaper to live with. If you are a startup where infra is the main bill, Rust feels like a cheat code. You squeeze more requests out of the same metal and you delay the next AWS upgrade cycle. If you are a stressed product team with a thin on-call rotation, Go is mercy. You get fewer weird crashes at 2 A.M. because problems are simpler, logging is simpler, mental model is simpler. This is why engineers argue online and both are “right.” They are solving different pain. How they break under pressure Let us talk failure shape. This matters more than happy-path benchmarks. Go under stress Go falls apart gradually. You start dropping tail latency first. Then CPU pegs. Then GC and goroutine scheduling get noisy. Then queues form. Then alerts fire. It is not pretty, but you see it coming. You can throw a circuit breaker around the hot call and keep the rest of the app alive. You can also ship a hotfix fast because the codebase is, by design, boring. It looks something like this: +----------- request -----------+ | parse -> work -> marshal | | goroutine per request | | shared cache / mutex hot spot| +------------------------------+

          |
          v
      CPU Pressure
          |
          v
    tail p95 explodes
          |
          v
  scale replicas / patch

Slow death. Recoverable. Rust under stress Rust does not fall apart the same way. It resists. The memory safety rules and ownership model force you to design data flow early. You are not casually sharing mutable state across tasks. You are not cloning 7 MB structs by accident every time you pass them around. So you get this beautiful flat curve under burst. p95 stays calm. CPU is not melting. You feel like a hero. Until you need a change at 1 A.M. Because this is where Rust makes you pay upfront: you do not get accidental “good enough” concurrency. You get explicit concurrency. Which also means: you are now responsible for that design forever. You drew the shape. You maintain the shape. You will not hack around it in five minutes. A tiny code example of the pain trade Here is the style difference in spirit. Go version of a worker fan-out that enriches data, with a shared cache: type Item struct {

   ID   string
   Data string

} func enrich(items []Item) []Item {

   out := make([]Item, len(items))
   wg := sync.WaitGroup{}
   for i := range items {
       wg.Add(1)
       go func(idx int) {
           defer wg.Done()
           item := items[idx]
           item.Data = fetchData(item.ID) // may hit cache/map
           out[idx] = item
       }(i)
   }
   wg.Wait()
   return out

} Readable. Shippable. Easy to tweak under pressure. Rust version doing similar fan-out safely, without data races: use futures::stream::{self, StreamExt};

  1. [derive(Clone)]

struct Item {

   id:   String,
   data: String,

} async fn enrich(items: Vec<Item>) -> Vec<Item> {

   stream::iter(items)
       .map(|mut item| async move {
           let extra = fetch_data(&item.id).await; // no shared mutable write
           item.data = extra;
           item
       })
       .buffer_unordered(32)
       .collect()
       .await

} This Rust is fast, safe, and pretty clean actually. But notice something subtle: we already had to think async, lifetimes, move semantics, buffer size, concurrency window. That thinking up front is what saves CPU later. But that thinking up front is also why new engineers move slower at the start. Problem: Go lets you move fast without deep concurrency design.
Change: Rust forces you to design concurrency before you earn compile.
Result: Rust runs leaner in production because bugs never made it alive. That is the trade in 3 lines.


So which one should you pick? Here is the simplest rule I have found. Choose Go when:

  • You have a small team and a big feature surface.
  • On-call is shared by generalists, not specialists.
  • You need to ship new endpoints weekly, not quarterly.
  • Most incidents are “bad business logic,” not “kernel-level meltdown.”

This is most product teams. Choose Rust when:

  • Infra spend is already painful and leadership actually cares.
  • Latency is part of the product promise.
  • You are hitting CPU ceilings at burst even after caching, pooling, batching.
  • You can afford sharper review cycles and slower onboarding to get runtime efficiency.

This is high-scale data pipes, edge compute, hot paths, brokers, inference runners. Notice what I did not say. I did not say “Rust is faster so always use Rust.”
I did not say “Go is easier so always use Go.” The honest line is uglier: Rust saves hardware.
Go saves humans.


The part nobody says out loud If you are senior, this is your real job: not picking the smartest language, but picking who suffers. You can make AWS suffer.
Or you can make your team suffer. Rust pushes pain forward, into development time and review time. After that, production is calm and cheap. Go pushes pain later, into runtime and scaling cost. After that, humans can respond fast and stay sane. Both are valid. Both are honest. So do the grown-up thing. Pick your pain while you are awake.

Read the full article here: https://medium.com/@kp9810113/rust-eats-fewer-cores-go-eats-fewer-weekends-e494db5b9969