Faisalaffan Blog
|
🇬🇧
Faisalaffan Blog
Engineering17 min read

Why I Ditched Rust, Node, and Python for Go: The Brutally Honest Benchmark Data

A head-to-head benchmark of 7 popular programming languages: performance, memory, concurrency, developer experience, and the hiring market. Discover exactly why Go dominates production at millions of requests — and why I almost jumped ship to Rust (but didn't).

Faisal Affan
Faisal Affan
Published
Gallery
1 / 3

Why I Choose Go as My Primary Stack

"Simplicity is the ultimate sophistication." — Leonardo da Vinci

Disclaimer

This article is not about "Go is the best language ever" — but rather about why Go is perfectly suited as a primary stack for my context and needs, and why I remain open to other stacks. All programming languages are tools — choose the right one for the right problem.


🎯 1. The Decision

Every engineer inevitably hits this point: choosing a primary stack. It doesn't mean shutting yourself off from other technologies, but defining one language as your "home base" — the place where you are most productive, understand the ecosystem the deepest, and are most confident in delivering production-grade software.

After years of working with various languages, I chose Go (Golang) as my primary stack. Here are the reasons, complete with benchmark data.


📊 2. Benchmark: Go vs The Rest

Compilation Speed

One of the first things that made me fall in love with Go: blazing fast compilation.

xychart-beta
    title "Compilation Speed (Medium Project)"
    x-axis ["Go", "C#/.NET", "TypeScript", "Java", "Rust"]
    y-axis "Seconds" 0 --> 120
    bar [3, 10, 12, 20, 75]
Language Compile Time Notes
Go ~2-5 seconds Incremental, extremely fast
Rust ~30-120 seconds Borrow checker + heavy optimization
Java ~10-30 seconds Separate JIT warmup
.NET (C#) ~5-15 seconds Decent, but still loses out
TypeScript (Node) ~5-20 seconds (tsc) Depends on config & size
PHP N/A (interpreted) No compile step
Python N/A (interpreted) No compile step
Why So Fast?

Go was designed from the ground up to compile fast. Rob Pike and the Google team built Go because they were frustrated by C++ compile times that could reach 45 minutes in Google's monorepo. Go's dependency resolution is linear — not exponential like C/C++.

Memory Efficiency

This is critical for production — especially when running dozens of microservices on Kubernetes.

Language/Runtime Memory Usage (Idle) Relative to Go
Go ~8-12 MB 1x (baseline)
Rust ~2-5 MB 0.4x (winner)
PHP (Laravel) ~20-40 MB/worker 3x
Node.js ~30-50 MB 4x
Python (FastAPI) ~30-50 MB 4x
.NET (ASP.NET) ~50-100 MB 8x
Java (Spring Boot) ~150-300 MB 25x
The Sweet Spot

Rust indeed wins in memory, but Go provides the best balance between memory efficiency and ease of development. You get ~90% of Rust's efficiency with ~30% of the effort.

HTTP Request Throughput

Benchmark using a simple JSON response scenario ({"message": "hello"}):

xychart-beta
    title "HTTP Throughput (requests/sec)"
    x-axis ["Rust Actix", "Go Fiber", "Go net/http", ".NET Minimal", "Java WebFlux", "Node Fastify", "PHP Swoole", "Python uvicorn"]
    y-axis "Requests/sec (thousands)" 0 --> 700
    bar [650, 550, 450, 350, 300, 150, 120, 40]
Language/Framework Requests/sec Avg Latency
Rust (Actix-web) ~650,000 ~0.15ms
Go (Fiber) ~550,000 ~0.18ms
Go (net/http) ~450,000 ~0.22ms
.NET (ASP.NET Minimal) ~350,000 ~0.28ms
Java (Spring WebFlux) ~300,000 ~0.33ms
Node.js (Fastify) ~150,000 ~0.67ms
PHP (Swoole) ~120,000 ~0.83ms
Python (uvicorn) ~40,000 ~2.5ms

Cold Start Time

Crucial for serverless and container-based deployments:

xychart-beta
    title "Cold Start Time (ms) - Lower is Better"
    x-axis ["Go", "Rust", "PHP", "Node.js", "Python", ".NET", "Java"]
    y-axis "Milliseconds" 0 --> 5000
    bar [8, 8, 75, 200, 350, 1000, 3500]
Serverless & Kubernetes

In the era of serverless and Kubernetes, cold start time is no trivial matter. Go and Rust lead by a massive margin here because they compile down to a static binary with no runtime dependencies. Java, with its 3.5 second cold start, can be a showstopper for serverless workloads.

Binary Size & Deployment

Language Binary/Artifact Size
Rust ~5-10 MB
Go ~10-15 MB
Java ~30-80 MB (JAR)
.NET ~30-60 MB
Python ~30-100 MB (venv)
PHP ~50-150 MB (vendor)
Node.js ~50-200 MB (node_modules)

Go outputs a single static binary — no runtime needed, no dependency managers required in production:

Dockerfile
FROM scratch
COPY myapp /app
ENTRYPOINT ["/app"]

That's it. A 15 MB Docker image. Compare that to Node.js which requires a node_modules folder sitting at 200 MB.


⚡ 3. Beyond Numbers: Why Go Wins for Me

Benchmarks are only half the story. Here are the qualitative factors that make Go my top choice:

Simplicity by Design

main.go
package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, `{"message": "Hello, World!"}`)
    })
    http.ListenAndServe(":8080", nil)
}

10 lines. No annotations, no DI frameworks, no XML configs.

Concurrency: Goroutines vs Threads

graph LR
    subgraph "Go - Goroutines"
        G1["Goroutine 1<br/>~2-8 KB"] --- G2["Goroutine 2<br/>~2-8 KB"]
        G2 --- G3["Goroutine 3<br/>~2-8 KB"]
        G3 --- G4["..."]
        G4 --- G5["Goroutine 1,000,000<br/>~2-8 KB"]
    end

    subgraph "Java/.NET - OS Threads"
        T1["Thread 1<br/>~1 MB"] --- T2["Thread 2<br/>~1 MB"]
        T2 --- T3["Thread 3<br/>~1 MB"]
        T3 --- T4["..."]
        T4 --- T5["Thread ~5,000<br/>(OS limit)"]
    end

    style G1 fill:#d1fae5,stroke:#10b981,color:#000
    style G2 fill:#d1fae5,stroke:#10b981,color:#000
    style G3 fill:#d1fae5,stroke:#10b981,color:#000
    style G4 fill:#d1fae5,stroke:#10b981,color:#000
    style G5 fill:#d1fae5,stroke:#10b981,color:#000
    style T1 fill:#fee2e2,stroke:#ef4444,color:#000
    style T2 fill:#fee2e2,stroke:#ef4444,color:#000
    style T3 fill:#fee2e2,stroke:#ef4444,color:#000
    style T4 fill:#fee2e2,stroke:#ef4444,color:#000
    style T5 fill:#fee2e2,stroke:#ef4444,color:#000
concurrency.go
// Goroutines + Channels = Elegant concurrency
func processOrders(orders []Order) []Result {
    results := make(chan Result, len(orders))

    for _, order := range orders {
        go func(o Order) {
            results <- processOrder(o)
        }(order)
    }

    var processed []Result
    for range orders {
        processed = append(processed, <-results)
    }
    return processed
}
1 Million Concurrent Connections

Goroutines use only a ~2-8 KB stack (vs ~1 MB per thread in Java/.NET). This means you can run millions of concurrent goroutines in a single process — using the same amount of memory Java would need for just ~50 threads.

Built-in Tooling

PropertyTypeDescription
go fmt formatter
default: built-in
Auto-format code — no styling debates. Complete uniform formatting globally for all Go code.
go vet linter
default: built-in
Static analysis — catches bugs before runtime.
go test testing
default: built-in
Testing + benchmarking + coverage — no additional frameworks needed.
go mod package manager
default: built-in
Dependency management — reproducible builds, zero lock-file drama.
go build compiler
default: built-in
Cross-compilation built-in — compile for any OS/arch from anywhere easily.
go generate codegen
default: built-in
Code generation — automate boilerplate seamlessly, without hidden magic.

Need to cross-compile from a Mac to Linux?

Terminal
GOOS=linux GOARCH=amd64 go build -o myapp

Done. No Docker dependencies, no VMs, no convoluted CI pipelines needed.

Clean Project Structure

  • myservice
    • cmd
      • api
        • main.go
    • internal
      • handler
        • user.go
        • order.go
      • service
        • user.go
        • order.go
      • repository
        • user.go
        • order.go
    • go.mod
    • go.sum
    • Dockerfile

There is no node_modules/, no 200 MB vendor/ folder, no config hell. It's clean, predictable, and easy to navigate.


🔍 4. Where Others Shine: Honest Comparison

Choosing Go doesn't mean blinding myself to the strengths of other languages. Every language has its own sweet spot:

🦀 Rust — When Safety & Performance are Non-Negotiable

Strengths:

  • Memory safety without a garbage collector (ownership system)
  • Zero-cost abstractions — C/C++ tier performance
  • Incredibly powerful pattern matching & algebraic types
  • First-class WebAssembly support

Trade-offs:

  • Very steep learning curve (~6-12 months to become highly productive)
  • Long compile times (~30-120 seconds)
  • Boilerplate for otherwise simple concepts (lifetime annotations)

When to choose Rust:

Use Case Reason
Embedded systems Zero-cost abstractions, no runtime
OS-level programming Memory safety without a GC
Game engines Performance matching C++
Performance-critical components When nanoseconds matter
WebAssembly First-class WASM support

Rust is not a competitor to Go — they solve completely different paradigms. Go is for the application-level, Rust is for the system-level.

🟢 Node.js/TypeScript — When Full-Stack Matters

Strengths:

  • One language rules both frontend frontend + backend
  • Massive npm ecosystem (~2.5 million packages)
  • Great for real-time apps (WebSocket, SSE)
  • TypeScript offers fantastic type safety

Trade-offs:

  • Single-threaded (event loop) — CPU-bound tasks are highly problematic
  • The inevitable node_modules black hole
  • Callback/Promise complexities in massive codebases

When to choose Node.js:

Use Case Reason
Full-stack web apps One language, one unifying ecosystem
Real-time features Native WebSocket, SSE
BFF (Backend for Frontend) Close alignment with the frontend team
Rapid prototyping The incredibly vast npm ecosystem
🐍 Python — When Data is King

Strengths:

  • The world's absolute best ML/AI ecosystem (PyTorch, TensorFlow, scikit-learn)
  • Elite Data science & analytics processing (pandas, numpy, matplotlib)
  • Easy, highly-readable scripting
  • An enormous scientific community

Trade-offs:

  • GIL (Global Interpreter Lock) — severely limits true parallelism
  • Performance is ~10-100x slower than Go
  • Extremely fragmented packaging environments (pip, conda, poetry, uv)

When to choose Python:

Use Case Reason
Machine Learning PyTorch, TensorFlow, Hugging Face
Data pipelines pandas, Spark, Airflow
Scripting & automation Readable, lightning fast to write
Prototyping Insanely fast iterations
☕ Java / 🟣 .NET — When Enterprise Demands It

Strengths:

  • Unbelievably mature ecosystem & tooling (20+ years of pedigree)
  • Elite Enterprise-grade frameworks (Spring, ASP.NET)
  • Strong typing & well-documented OOP patterns
  • An overwhelmingly massive talent pool

Trade-offs:

  • Extreme verbose boilerplate (AbstractSingletonProxyFactoryBean, anyone?)
  • Unbelievably heavy memory footprint (JVM: 150-300 MB idle)
  • Painfully slow startup (JVM: 2-5 seconds for a single cold start)
  • Natural tendency for severe over-engineering

When to choose Java/.NET:

Use Case Reason
Enterprise systems Unmatched maturity, extremely battle-tested
Legacy integration A heavily established legacy ecosystem
Large team projects Tried and true patterns
Android native Java/Kotlin native capabilities
🐘 PHP — When Web is Everything

Strengths:

  • The remarkably excellent Laravel ecosystem
  • Unbelievably simple deployment (shared hosting friendly)
  • Huge and extremely helpful community
  • PHP 8.x is far more modern (fibers, enums, typed properties)

Trade-offs:

  • Extremely limited performance for high-throughput conditions
  • Wildly inconsistent standard library (needle/haystack ordering nightmares)
  • Restrictive per-request model (though tools like Swoole/RoadRunner change this)

When to choose PHP:

Use Case Reason
Content-heavy websites WordPress, dominating the CMS ecosystem
Rapid web development Laravel's insanely high productivity
Small-medium projects Fast, cheap, unbelievably easy to deploy
E-commerce Magento, WooCommerce

🧠 5. My Stack Philosophy

Core Principle

"Use the right tool for the right job." — Every single programming language is a tool. A carpenter doesn't just hold a hammer — they own saws, drills, chisels, and sandpaper. They each serve their own unique, specific purpose.

Decision Framework

How I select a stack for any given project:

graph TD
    A["🆕 New Project"] --> B{"What's the Domain?"}

    B -->|"Backend / API / CLI / Infra"| C["🐹 Go"]
    B -->|"Frontend / UI"| D["⚛️ TypeScript + React"]
    B -->|"Mobile"| E["📱 Flutter / Dart"]
    B -->|"ML / Data Science"| F["🐍 Python"]
    B -->|"System / Embedded"| G["🦀 Rust"]
    B -->|"Quick Script"| H["🐍 Python / 🐚 Bash"]
    B -->|"Legacy Maintenance"| I["🐘 PHP / ☕ Java"]

    C --> J{"Require ultra-low latency?"}
    J -->|"Yes"| G
    J -->|"No"| C

    style A fill:#dbeafe,stroke:#3b82f6,color:#000
    style C fill:#d1fae5,stroke:#10b981,color:#000
    style D fill:#e0e7ff,stroke:#6366f1,color:#000
    style E fill:#fce7f3,stroke:#ec4899,color:#000
    style F fill:#fef3c7,stroke:#f59e0b,color:#000
    style G fill:#fee2e2,stroke:#ef4444,color:#000
    style H fill:#f3e8ff,stroke:#a855f7,color:#000
    style I fill:#f1f5f9,stroke:#64748b,color:#000
    style J fill:#fef3c7,stroke:#f59e0b,color:#000

My Toolbox

🐹 Primary — Go (90% Backend Work)

API services, microservices, CLI tools, infrastructure tooling, system programming. Go seamlessly handles 90% of my backend needs with absolutely staggering efficiency.

⚛️ Frontend — TypeScript + React/Next.js

Undeniably the best ecosystem for modern UI. Flawless type safety, clean component-based architectures, and an overwhelmingly massive community.

📱 Mobile — Flutter/Dart

Cross-platform, utilizing a single codebase. Delivering near-native performance while keeping development speeds at an absolute high.

🐍 Data & ML — Python

The absolute king of machine learning, data pipelines, and scripting. There is simply no better alternative for this specific domain.

🦀 Performance-Critical — Rust

When Go simply isn't fast enough — for embedded hardware, WASM, or extraordinarily compute-heavy components.

The 80/20 Rule

pie title "My Stack Usage Distribution"
    "Go (Backend)" : 50
    "TypeScript (Frontend)" : 20
    "Dart (Mobile)" : 15
    "Python (Data/Script)" : 10
    "Others (Rust/PHP/Java)" : 5

Go reliably handles 80% of my needs with exactly 20% of the complexity. That is an absolutely flawless sweet spot. The second I need something far beyond what Go offers natively, I swap to the right tool — without ego, and completely without fanatical brand loyalty.


📈 6. Real-World Impact

A few metrics sourced from live production systems I personally built using Go:

Metric Before (Node.js/PHP) After (Go) Improvement
Response Time (p99) ~250ms ~15ms 16x faster
Memory per Instance ~512 MB ~50 MB 10x less
Docker Image Size ~350 MB ~18 MB 19x smaller
Cold Start ~3s ~10ms 300x faster
Monthly Infra Cost ~$500 ~$80 6x cheaper
Cost Impact

The numbers presented above are sourced directly from real-world production experiences. Go's extreme resource efficiency directly translates to truly significant infrastructure cost savings. If you run 20+ microservices, a differential of ~460 MB of memory per instance essentially means you can run 10x the number of services on exactly the same hardware.

graph LR
    subgraph "Before: Node.js/PHP Stack"
        A1["Service 1<br/>512 MB"] --- A2["Service 2<br/>512 MB"]
        A2 --- A3["Service 3<br/>512 MB"]
        A3 --- A4["= 1.5 GB<br/>for 3 services"]
    end

    subgraph "After: Go Stack"
        B1["Service 1<br/>50 MB"] --- B2["Service 2<br/>50 MB"]
        B3["Service 3<br/>50 MB"] --- B4["Service 4<br/>50 MB"]
        B5["Service 5<br/>50 MB"] --- B6["..."]
        B6 --- B7["Service 30<br/>50 MB"]
        B7 --- B8["= 1.5 GB<br/>for 30 services"]
    end

    style A1 fill:#fee2e2,stroke:#ef4444,color:#000
    style A2 fill:#fee2e2,stroke:#ef4444,color:#000
    style A3 fill:#fee2e2,stroke:#ef4444,color:#000
    style A4 fill:#fee2e2,stroke:#ef4444,color:#000
    style B1 fill:#d1fae5,stroke:#10b981,color:#000
    style B2 fill:#d1fae5,stroke:#10b981,color:#000
    style B3 fill:#d1fae5,stroke:#10b981,color:#000
    style B4 fill:#d1fae5,stroke:#10b981,color:#000
    style B5 fill:#d1fae5,stroke:#10b981,color:#000
    style B6 fill:#d1fae5,stroke:#10b981,color:#000
    style B7 fill:#d1fae5,stroke:#10b981,color:#000
    style B8 fill:#d1fae5,stroke:#10b981,color:#000

🏁 7. Conclusion

TL;DR

Choosing Go isn't about calling other languages bad. All programming languages are simply tools — and the great tools are chosen strictly based on needs, zero fanaticism allowed.

Go became my permanent primary stack because it delivers:

C/Rust-Tier Performance with Python-level development speed. **The best

of both worlds.**

Unmatched Simplicity Built on only 25 keywords. Flawless code that is

profoundly readable, maintainable, and highly debuggable by anyone on the team.

First-Class Concurrency Using Goroutines + Channels. Pushing millions of

concurrent connections gracefully with minimal memory overhead.

Effortless Deployment Generating exactly one single static binary.

COPY and you are done. An impossibly lean 15 MB Docker image.

Incredible Cost Efficiency Providing resource efficiency that directly

and powerfully translates to rapid infrastructure cost savings — bringing everything down to 6x cheaper in production.

Yet I still write TypeScript for the frontend, Dart on mobile, Python for complex data workflows, and I fully remain open to deploying Rust for use cases that require absolutely monstrous performance.

The absolute best language is the one that solves your problem effectively. Go just happens to be that exact language for the vast majority of my work. And I will never hesitate to switch layers when the context aggressively demands it.

Because ultimately, we aren't "Go developers" or "Java developers" — we are engineers who wield the best tools available to solve incredibly specific problems.

Topics:#Go#Rust

Related stories

Engineering
Engineering
Integer to Roman: The Greedy Mapping That Always Shows Up in Interviews

Integer to Roman: The Greedy Mapping That Always Shows Up in Interviews

Breaking down the integer to Roman numeral conversion problem — from basic Roman numeral rules, the subtractive notation pitfalls (IV, IX, XL...), to a clean, interview-ready greedy O(1) solution. Complete with mapping table, step-by-step visualization, and why this approach is optimal given the 1-3999 constraint.
Faisal Affan
Faisal Affan
Published
Engineering
Best Time to Buy and Sell Stock: From Brute Force to One-Pass O(n)

Best Time to Buy and Sell Stock: From Brute Force to One-Pass O(n)

Master the classic coding interview problem 'Best Time to Buy and Sell Stock' with step-by-step explanation — from the naive brute force approach, recognizing patterns, to the optimal one-pass O(n) solution using sliding window / two-pointer technique. Complete with array visualization, complexity analysis, and the intuition behind every decision.
Faisal Affan
Faisal Affan
Published
Engineering
Services I've Handled — Full-Stack Engineering, Architecture & Product Strategy

Services I've Handled — Full-Stack Engineering, Architecture & Product Strategy

A comprehensive list of professional services I've delivered across multiple industries: IoT fleet management, fintech core banking integration, cyber insurance platforms, education technology, and enterprise digital transformation. Each service backed by real production systems still running today.
Faisal Affan
Faisal Affan
Published
Engineering
Portfolio Strategy by Career Level — Junior to CTO

Portfolio Strategy by Career Level — Junior to CTO

Your portfolio should reflect your seniority level, not just list every project you've touched. Junior engineers sell potential and fundamentals. Senior engineers sell depth and architecture. Staff engineers sell leverage. CTOs sell business outcomes. Here's exactly what to put in your portfolio at each level — with concrete examples and a universal format you can use today.
Faisal Affan
Faisal Affan
Published