After 21 years of Java development, I rebuilt our short-video platform backend in Go, Rust, and Kotlin (Ktor) to make an informed decision about our production stack. Same API across all four: authentication, video feed, follow system, S3 uploads, Redis caching. Here are the results.
Performance summary
Throughput (wrk, 200 concurrent, 10s, health endpoint)
| Java | Go | Rust | Kotlin | |
|---|---|---|---|---|
| QPS | 88K | 100K | 210K | 99K |
Latency distribution (200 concurrent)
| Go | Rust | Kotlin | |
|---|---|---|---|
| p50 | 1.79 ms | 0.70 ms | 1.60 ms |
| p99 | 6.13 ms | 1.48 ms | 15.36 ms |
Memory under 500 concurrent connections (RSS)
| Java | Go | Rust | Kotlin | |
|---|---|---|---|---|
| Peak | 372 MB | 60 MB | 33 MB | 650 MB |
Cold start (binary to first HTTP response)
| Java | Go | Rust | Kotlin | |
|---|---|---|---|---|
| Time | 2,714 ms | 69 ms | 153 ms | 914 ms |
Build time (clean, deps cached)
| Java | Go | Rust | Kotlin | |
|---|---|---|---|---|
| Time | 5.6s | 9.4s | 98.3s | 14.1s |
Things that surprised me
Kotlin nearly matches Go in throughput. Ktor 3.4 + Netty + JVM 21 Virtual Threads hit 99K QPS — within 1% of Go's 100K. JIT optimization on hot paths is real. But p99 latency (15ms) and memory (650 MB) remind you it's JVM under the hood.
Rust memory usage barely changes under load. 19 MB idle → 33 MB at 500 concurrent. No GC means no memory spikes from GC heap expansion. Go went from 25 to 60 MB, which is still excellent.
Go has the fastest cold start, not Rust. I assumed compiled-to-native would be roughly equal, but Go at 69ms vs Rust at 153ms. Tokio runtime initialization (thread pool, epoll) adds overhead.
Rust has the fewest lines of code. 4,179 lines in 37 files. Go was 4,929 (hand-written), Java 7,001, Kotlin 8,607 (includes tests). Rust's type system and enum/match expressiveness reduce boilerplate significantly.
The hardest part wasn't writing Rust — it was getting Kotlin to build. Gradle plugin compatibility issues consumed more time than both the Go and Rust implementations combined. In 2026, starting a new JVM project from scratch still feels unnecessarily painful.
What I actually chose
Rust (Axum) for production. The deciding factor was infrastructure cost: roughly half the memory means half the EC2 instances. The compile time penalty (~98s clean) is mitigated with BuildKit cache mounts in CI.
But I wouldn't universally recommend Rust. Here's my honest take:
- Need to ship fast with a team? Go. 9s builds, 69ms cold start, anyone can be productive in a week.
- Already invested in JVM but want modern? Kotlin + Ktor. Competitive throughput, 3x faster startup than Spring, great language ergonomics.
- Performance-critical core services, willing to invest? Rust. The numbers speak for themselves, but the learning curve and compile times are real costs.
- Enterprise ecosystem dependency? Java/Spring isn't going anywhere, but the performance gap is widening.
Methodology and caveats
- Single machine: macOS Apple Silicon, Docker (OrbStack)
- DB: PostgreSQL 18 (Go/Rust/Kotlin), MySQL 8.4 (Java only — not ideal)
- Benchmark: health endpoint only (no DB queries), wrk 4 threads
- Java implementation has different API paths and wasn't fully feature-equivalent
- No GraalVM native-image tested (would improve Java/Kotlin startup and memory)
- Kotlin JIT may not have been fully warmed up
Happy to answer questions or share specific details about any of the implementations.
Top comments (0)