Why We Built Axon in Rust, Not Go or TypeScript
Our Rust blockchain infrastructure for Axon was not the obvious choice. We are a TypeScript shop. Our team built Crowdia on Node.js, shipped KRAIN's backend in TypeScript, and I personally write more TypeScript in a week than most engineers write in a month. When we started scoping Axon, our L2 blockchain, the default assumption was that we'd reach for what we knew. We didn't. This post explains exactly why, what it cost us, and whether we'd make the same call again.
The short answer: yes, we would. But the reasoning is more specific than "Rust is fast" and the tradeoffs are more painful than the blog posts make it sound.
The Baseline: What TypeScript and Go Actually Give You
Before comparing, you need a clear picture of what each language genuinely offers in a backend infrastructure context, stripped of the hype.
TypeScript in a blockchain context means you're running on V8, which is a garbage-collected runtime. The GC is excellent for most web workloads. For consensus-critical paths where you need sub-millisecond, deterministic latency, it becomes a liability. V8's GC pauses are not predictable under memory pressure, and in a high-throughput block validation loop, memory pressure is constant. We saw this firsthand on an early Axon prototype: block validation times that averaged 1.2ms would occasionally spike to 18ms with no code change, purely from GC scheduling. That's a hard ceiling on what you can promise validators.
Beyond GC, TypeScript's type system does not extend to memory layout. You cannot express ownership, you cannot guarantee that a buffer passed to a signing function won't be mutated elsewhere, and you cannot model zero-copy deserialization in a way the runtime will actually enforce. The types are erased at compile time. You're writing documentation, not contracts.
Go is a more honest competitor. No V8, compiled to native, and the GC is genuinely impressive for most use cases. Go's concurrency model via goroutines is clean and its standard library is strong. Several production blockchains, including parts of the Ethereum ecosystem's tooling, run on Go. The go-ethereum client is the reference implementation for a reason.
The problem with Go for our specific requirements came down to two things. First, Go's GC, while better than V8's, still introduces non-deterministic pauses. The Go team has done tremendous work reducing GC latency (sub-millisecond in many cases since Go 1.18), but "usually sub-millisecond" is not the same as "provably bounded." In a consensus system where timing affects fork resolution, "usually" is not a specification. Second, Go gives you no way to express memory ownership at the type level. You can write safe Go, but the compiler cannot prove you did. A data race in a validator's signing path is a catastrophic failure mode, and Go's race detector is a testing tool, not a compile-time guarantee.
Why Rust Became Non-Negotiable for Axon's Core
Axon is an L2 blockchain. Its core responsibilities include block production, transaction validation, state root computation, and validator coordination. Every one of those paths has properties that make Rust's guarantees directly relevant, not abstractly appealing.
Memory safety without a GC. Rust's ownership model means the compiler proves at build time that memory is accessed safely. There is no runtime overhead for this guarantee. No GC pauses, no reference counting cycles, no surprise heap allocations in a hot path. When we're computing a Merkle root over 10,000 transactions in a single block, we need that computation to take the same amount of time on the 10,000th call as it did on the first. Rust gives us that. TypeScript and Go cannot make that promise.
Fearless concurrency. Axon's validator nodes handle concurrent transaction ingestion, block proposal, and peer communication simultaneously. Rust's type system makes data races a compile error. The Send and Sync traits are not documentation, they are enforced constraints. When our team was building the mempool manager, we introduced a concurrency bug in a review cycle. The compiler caught it before the code ever ran. In Go, that bug would have been a race condition waiting for a production load spike to surface it.
Predictable performance at the tail. The metric that matters for blockchain infrastructure is not average latency, it's p99 and p999. A validator that processes blocks in 1.2ms on average but spikes to 18ms at p99 is a validator that causes chain instability. Our Rust blockchain infrastructure consistently shows p999 block validation times within 2x of the median. We have not achieved that in any other language we've tested on equivalent hardware.
Zero-cost abstractions for cryptographic primitives. Axon uses RustCrypto crates for hashing and signature verification. These implementations compile down to the same machine code you'd write by hand in C, with the type safety layer on top. The sha2 crate with the asm feature enabled uses AVX2 intrinsics on our AMD Strix Halo development rigs and produces throughput that matches hand-tuned C benchmarks. You cannot get that in TypeScript. You can FFI to native code, but then you've left the safety model entirely.
The Comparison That Actually Matters: Rust vs. Go for Blockchain Backend
Since TypeScript was ruled out early for consensus paths (we still use it for Axon's RPC gateway and developer tooling), the real decision was Rust versus Go. Here's the direct breakdown across the dimensions that mattered to us.
| Dimension | Go | Rust |
|---|---|---|
| GC pauses in hot paths | Yes, usually <1ms but unbounded | None |
| Compile-time memory safety | No (race detector is runtime) | Yes |
| Concurrency data race prevention | Runtime detection only | Compile-time enforced |
| Ecosystem for crypto primitives | Moderate | Strong (RustCrypto, arkworks) |
| Learning curve for TypeScript team | 2-3 weeks to productivity | 3-4 months to real comfort |
| Compile times | Fast (seconds) | Slow (minutes on full rebuild) |
| Toolchain maturity | Excellent | Excellent (cargo is outstanding) |
| FFI for C libraries | Straightforward | Straightforward but unsafe blocks required |
| Community blockchain projects | go-ethereum, Tendermint | Solana, Polkadot, Near, Lighthouse |
The learning curve row is where I need to be honest. We underestimated it badly.
The Painful Learning Curve Nobody Warns You About
Every Rust article written by Rust enthusiasts mentions the borrow checker as "challenging at first." That phrasing does not prepare a TypeScript team for the actual experience.
Our first month on Axon's core was brutal. Engineers who had shipped production systems for years were blocked by compiler errors they didn't understand. The lifetime annotation system, specifically when you start passing references across async boundaries with Tokio, produces error messages that require understanding concepts that have no equivalent in TypeScript or Go. We had one engineer spend three days on a single function signature involving a future that held a reference to a struct that itself contained a reference-counted pointer to shared state. The compiler was right. The code was wrong. But understanding why took longer than the fix.
The async story in Rust is also genuinely harder than Go's goroutines. Tokio is excellent, and the Tokio documentation is some of the best technical writing in the open source world. But async Rust requires you to understand the underlying state machine that the compiler generates, at least conceptually, in a way that Go's goroutines deliberately hide from you. For a team coming from async/await in TypeScript, the mental model transfer is incomplete. TypeScript's async is cooperative and GC-managed. Rust's async is zero-allocation and requires you to think about pinning.
We lost approximately six weeks of velocity in the first quarter of Axon's development to the learning curve. That is a real cost. If Axon had been a standard CRUD API, it would not have been worth it. Because Axon is consensus infrastructure where correctness and performance are existential requirements, it was worth it. But I want to be clear that "worth it" came at a price we paid in real time, with real engineers, on a real deadline.
Where We Still Use TypeScript (And Why That's Fine)
The Rust blockchain infrastructure handles Axon's consensus engine, block validation, state management, and the p2p networking layer. TypeScript handles everything else: the JSON-RPC endpoint that external developers call, the indexer that reads finalized blocks and writes to Postgres, the admin dashboard, and all of our internal tooling.
This is the architecture I'd recommend to any team in a similar position. Rust where correctness and performance are provably required. TypeScript where developer velocity and ecosystem breadth are the primary constraints. The boundary between them is clean in Axon because we defined it early and stuck to it. The RPC gateway does not share memory with the consensus engine. It communicates over a local socket. That boundary let us keep our TypeScript engineers productive on real features while the Rust engineers built the core.
The mistake I see other teams make is treating this as an all-or-nothing choice. It isn't. Your blockchain backend can be Rust at the core and TypeScript at the edges. The two can coexist cleanly if you design the interfaces deliberately.
The Verdict
If you're building an L2 blockchain, a validator client, or any system where you need provable memory safety, zero GC pauses in consensus paths, and compile-time concurrency guarantees, choose Rust. Go is a fine language and I respect what the go-ethereum team has built, but Go cannot give you the compiler as a proof system. TypeScript cannot give you native performance without a GC.
If you're building a blockchain explorer, a wallet backend, a developer API, or tooling around an existing chain, TypeScript or Go are both sensible choices. The overhead of Rust's learning curve and compile times is not justified when you're writing a REST API that calls an RPC endpoint.
We chose Rust for Axon's core because the requirements made it non-negotiable, not because we were chasing novelty. The six weeks of lost velocity hurt. The compile times (a full Axon rebuild takes around four minutes on the Flow Z13) are annoying every single day. The p999 validation latency numbers and the absence of a single memory-safety bug in production are why we'd make the same call again.
The borrow checker is not your enemy. It's the most honest reviewer on your team. It just takes a few months to learn its language.