Choosing Rust Over Go for New Services

architecturerustperformancesystems-programming

As we architected a new high-throughput data processing layer, we needed to select a systems language that could handle extreme concurrency and memory safety without the unpredictable latency of a garbage collector.

Adopt Rust as the standard for performance-critical backend services

Standardize on Go (Golang)

Pros
  • Extremely fast compilation times
  • Lower learning curve for existing engineers
  • Excellent standard library for networking
Cons
  • Garbage Collection (GC) pauses can cause latency spikes
  • Lack of 'fearless concurrency' (data races still possible)
  • Less control over low-level memory layout

C++ with Modern Standards

Pros
  • Ultimate control over hardware
  • Mature ecosystem for systems programming
Cons
  • High risk of memory safety vulnerabilities
  • Steep learning curve and complex build systems
  • Manual memory management leads to higher maintenance costs

While Go is excellent for general microservices, Rust’s ownership model provides compile-time guarantees against data races and memory leaks without the overhead of a Garbage Collector. For our specific use case—processing millions of events per second—the zero-cost abstractions and predictable performance profile of Rust outweighed the faster initial development cycle of Go.

Context and Background

Our existing Go-based services were performing well for standard CRUD operations. However, as we moved into real-time genomic processing and high-frequency data ingestion, we hit several bottlenecks:

  • Tail Latency: The Go Garbage Collector (GC) introduced non-deterministic “Stop the World” pauses that affected our p99 response times.
  • Resource Efficiency: We wanted to maximize the utility of our CPU clusters by reducing the memory footprint per instance.
  • Safety: We encountered several production deadlocks and data races that were difficult to debug in Go’s concurrent model.

Implementation

We introduced Rust into our ecosystem using a targeted strategy:

  1. The ‘Greenfield’ Rule: Only new, performance-critical services were written in Rust to avoid disruptive rewrites.
  2. FFI and Interop: Used Foreign Function Interface (FFI) to allow our existing Python research scripts to call high-performance Rust binaries.
  3. Internal Mentorship: Established a “Rust Guild” to help developers navigate the borrow checker and ownership concepts through pair programming.
  4. Standardized Tooling: Adopted cargo and established a centralized crate registry for shared internal logic and types.

Results After 6 Months

  • Zero GC Overhead: Eliminated unpredictable latency spikes, leading to a 25% improvement in p99 latency across the data layer.
  • Memory Efficiency: Service memory usage dropped by approximately 50% compared to equivalent Go services.
  • Compile-Time Safety: Since the initial deployment, we have had zero production incidents related to null pointers or data races in the Rust services.

The decision has proven successful; while the learning curve was steeper, the reliability of the resulting services has significantly reduced our on-call burden.