Why we rewrote the core pipeline in Rust
22 August 2025 · James Okafor
We shipped the first version of RedFlag's hot path in Go. It worked, it was readable, and it got us to our first hundred customers. It also couldn't meet the latency targets we needed to compete at the top of the market. This is the story of the rewrite.
What the Go version looked like
The original pipeline was a fairly conventional Kafka consumer: pull events, enrich from Redis, fan out to three scoring goroutines, aggregate, publish. It was fast enough at low volumes — median latency around 800ms — but GC pauses became unpredictable under the event spikes we see at market open or after a product goes viral.
"800ms sounds fast until your customer tells you they blocked a transaction that already settled."
We tried tuning the GC, reducing allocations, switching to a pool-based design. We got to 520ms. Our target was under 400ms, and we knew we'd eventually want to be under 200ms. Go wasn't going to get us there without heroic effort we'd have to maintain forever.
What changed in Rust
The rewrite took one engineer about six weeks. The Rust version of the same pipeline runs at a median of 340ms today and we have a clear path to 180ms with one more optimisation pass. More importantly, the tail latency — p99 — dropped from 4.2 seconds in Go to 610ms in Rust. That's what actually hurt customers.
The ownership model forced us to be explicit about allocations in a way that caught several latent bugs. We also found that tokio with async/await gave us a mental model that was surprisingly close to the goroutine concurrency we'd had before — easier to reason about than we expected.
Would we do it again?
Yes, but only for the hot path. Our API layer, dashboard backend, and internal tooling are staying in Go. The hire pool is bigger, iteration is faster, and those services don't have the same latency demands. Rust is the right answer for one specific, critical, bounded problem — not a company-wide default.