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

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).
"Simplicity is the ultimate sophistication." — Leonardo da Vinci
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.
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.
Years of working with PHP, Python, Node.js, Java, Dart — eventually experimenting with Rust and .NET across various project scales.
Choosing Go wasn't based on hype, but on real-world experience building and operating systems in production.
Choosing Go as my primary doesn't mean I close myself off. I still use TypeScript, Python, Dart, and other languages as needed.
After years of working with various languages, I chose Go (Golang) as my primary stack. Here are the reasons, complete with benchmark data.
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 |
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++.
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 |
| Language/Runtime | Memory Under Load | Relative to Go |
|---|---|---|
| Go | ~20-50 MB | 1x (baseline) |
| Rust | ~10-30 MB | 0.5x (winner) |
| PHP (Laravel) | ~50-100 MB/worker | 3x |
| Node.js | ~80-200 MB | 4x |
| Python (FastAPI) | ~80-200 MB | 4x |
| .NET (ASP.NET) | ~100-300 MB | 6x |
| Java (Spring Boot) | ~300-800 MB | 12x |
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.
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 |
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]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.
| 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) |
| Language | Docker Image Size |
|---|---|
| Rust | ~10-15 MB (scratch) |
| Go | ~15-20 MB (scratch) |
| PHP | ~100-200 MB |
| .NET | ~150-250 MB |
| Node.js | ~150-300 MB |
| Java | ~200-400 MB |
| Python | ~150-400 MB |
Go outputs a single static binary — no runtime needed, no dependency managers required in production:
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.
Benchmarks are only half the story. Here are the qualitative factors that make Go my top choice:
25 keywords. No generics abuse, no annotation magic, no hidden behavior. What you see is what you get.
Goroutines + Channels = Elegant concurrency. ~2-8 KB per goroutine vs ~1 MB per thread in Java.
net/http, encoding/json, database/sql, crypto, testing — all production-ready without third-party dependencies.
go fmt, go vet, go test, go mod, go build — built-in, integrated, no config hell.
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.
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@GetMapping("/")
public String hello() {
return "{\"message\": \"Hello, World!\"}";
}
}Plus a pom.xml / build.gradle, application.properties, and a deep dependency tree.
const express = require("express");
const app = express();
app.get("/", (req, res) => {
res.json({ message: "Hello, World!" });
});
app.listen(8080, () => console.log("Server running"));Plus package.json, node_modules/ (~200 MB), and package-lock.json.
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// 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
}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.
| Property | Type | Description |
|---|---|---|
| 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?
GOOS=linux GOARCH=amd64 go build -o myappDone. No Docker dependencies, no VMs, no convoluted CI pipelines needed.
There is no node_modules/, no 200 MB vendor/ folder, no config hell. It's clean, predictable, and easy to navigate.
Choosing Go doesn't mean blinding myself to the strengths of other languages. Every language has its own sweet spot:
Strengths:
Trade-offs:
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.
Strengths:
Trade-offs:
node_modules black holeWhen 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 |
Strengths:
Trade-offs:
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 |
Strengths:
Trade-offs:
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 |
Strengths:
Trade-offs:
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 |
"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.
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:#000API services, microservices, CLI tools, infrastructure tooling, system programming. Go seamlessly handles 90% of my backend needs with absolutely staggering efficiency.
Undeniably the best ecosystem for modern UI. Flawless type safety, clean component-based architectures, and an overwhelmingly massive community.
Cross-platform, utilizing a single codebase. Delivering near-native performance while keeping development speeds at an absolute high.
The absolute king of machine learning, data pipelines, and scripting. There is simply no better alternative for this specific domain.
When Go simply isn't fast enough — for embedded hardware, WASM, or extraordinarily compute-heavy components.
pie title "My Stack Usage Distribution"
"Go (Backend)" : 50
"TypeScript (Frontend)" : 20
"Dart (Mobile)" : 15
"Python (Data/Script)" : 10
"Others (Rust/PHP/Java)" : 5Go 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.
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 |
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:#000Choosing 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:
of both worlds.**
profoundly readable, maintainable, and highly debuggable by anyone on the team.
concurrent connections gracefully with minimal memory overhead.
COPY and you are done. An impossibly lean 15 MB Docker image.
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.



