How Come Golang Is So Awesome
I do not use Go for everything, and I want to be honest about that up front.
My stack has a hierarchy. Shell handles most of what I do. Python takes over when shell gets too complex. Go is what I reach for when I am explicitly optimizing for performance. That last tier is what this post is about — because when performance is the goal, Go is awesome in a very specific, practical way.
My actual path: Shell, then Python, then Go
I started where most infrastructure people start: shell.
Shell is still my default for general tasks. It is everywhere, it glues things together, and for automating a box it is the fastest path from idea to working. Most of my day-to-day work never needs anything heavier, and I am not apologizing for that.
But shell stops scaling the moment a “script” grows a real data structure, needs tests, or has to behave the same on three slightly different systems. That is my signal to move up a tier. When shell gets too complex, I reach for Python.
Python is the middle of my stack and it earns that spot. It gives me real data structures, libraries for everything, and code I can read out loud. When a shell script is turning into a tangle of awk, sed, and subshells, rewriting it in Python is almost always the right call. I use it constantly for data work, glue, and anything where clarity matters more than raw speed.
Go enters when the goal changes. The moment I am explicitly optimizing for performance — real concurrency, fast startup, something that will sit in a hot path — that is when Go becomes the right tool. It is not my hammer for every nail.
The first time it clicked was a small internal tool. I ran go build, got one static binary, copied it to a server with nothing installed, and it just ran — no interpreter, no virtualenv, no dependency negotiation. For a tool that had to be fast and portable, that was exactly right.
A little history, because it explains the design
Go came out of Google in 2009, designed by people who had felt the pain of large C++ and Java codebases: slow builds, tangled dependencies, and concurrency that was easy to get wrong. The goal was not academic elegance — it was a language large teams could be productive in while still getting performance that mattered.
That origin shows. The spec is small enough to hold in your head. There is one obvious way to format code, and gofmt enforces it so nobody argues about braces. Concurrency is built into the language with goroutines and channels instead of bolted on as a library, and compilation is fast on purpose.
Go is opinionated in a way that removes decisions rather than adds them. For a long time it did not even have generics; they landed in Go 1.18 and the language is better for it, but the telling part is how long they waited. Go resists features, and that restraint is the feature.
Where Go is today
Go is the quiet backbone of modern performance-sensitive infrastructure. Docker and Kubernetes are written in Go. Terraform, Prometheus, Grafana, etcd, Consul, the CLIs for most major clouds, half the CNCF landscape — Go. If you work in cloud or platform engineering, you are already running Go all day. The ecosystem for what I care about — networking, HTTP, gRPC, observability, container tooling — is deep and battle-tested.
The toolchain has aged well too. Modules fixed dependency management, the standard library is genuinely good for anything network-facing, and cross-compilation is a single environment variable, so I build Linux ARM binaries from my laptop without a second thought.
Practical performance
This tier is the whole reason Go exists in my stack. It compiles to native machine code, so it starts instantly and a service handling HTTP traffic will happily saturate what you throw at it long before the language becomes your bottleneck. Startup time is near-zero, which matters more than people admit in a world of autoscaling and short-lived containers. This is exactly the gap that pushes me past Python: when a Python service’s import time and per-request overhead start showing up in the numbers, Go is the answer.
The garbage collector is the honest caveat. It is tuned for low latency and for the overwhelming majority of services you will never notice it, but it means Go is not the tool for hard real-time work where you cannot tolerate any pause.
The other quiet win is concurrency: goroutines are cheap, and spinning up tens of thousands of them to handle concurrent connections is normal, not heroic. For the network-bound, I/O-heavy workloads where I actually need performance, that model fits.
Productivity and ergonomics
Performance is the reason I reach for Go, but the ergonomics are why I do not resent it. Go code reads the same no matter who wrote it, and the language is small enough that onboarding is measured in days. I can drop into an unfamiliar Go codebase and understand it, because there are only so many ways to express something.
The explicit error handling gets criticized constantly — all those if err != nil blocks — and I have come around. Errors are values you handle in the open, not exceptions that tunnel up through layers you forgot about. When something breaks at 3 a.m., I would rather read verbose, obvious error handling than clever code that hides the failure path.
And the deployment story closes the loop: one static binary, no runtime, trivial containers. A Go service in a container is a FROM scratch image with a single file in it — the performance tier does not cost me operational pain.
The honest Go vs Rust section
I like Rust. I am not in the tribal camp that has to pick one and insult the other.
Rust wins on raw performance and control. No garbage collector, no runtime pauses, memory safety enforced at compile time by the borrow checker. If you are writing a database engine, a real-time system, an embedded target, or anything where you need to account for every allocation and cannot tolerate GC pauses, Rust is the better tool. It also gives you fearless concurrency with compile-time guarantees Go does not offer — Go will happily let you write a data race and hand you a runtime detector for it.
Go wins on speed of development, simplicity, and getting a competent team productive fast. The borrow checker that makes Rust safe also makes it slower to write and steeper to learn. Go’s compile times are dramatically faster, which keeps the feedback loop tight. For most performance-sensitive network and infrastructure work, Go gets me to a shipped, maintainable result faster, and the performance is more than enough.
So how I choose, sitting above the shell-then-Python tiers: if I need performance for a network service, a CLI, a controller, glue between cloud APIs, or anything a team will maintain together — Go. If I need to account for every allocation, hit hard real-time targets, or build a long-lived library where correctness guarantees pay for the effort — Rust. The use case answers the question.
Where it lands in real work
Framed through that hierarchy, here is where Go specifically earns the climb:
- Cloud: when tooling against cloud APIs has to be fast and ship as one binary, Go beats a Python script that needs an interpreter on every host.
- Backend and microservices: the sweet spot — fast startup, cheap concurrency, and tiny containers for services that scale horizontally and where latency is measured.
- Infrastructure tooling: operators and controllers that run in hot paths and have to be reliable where I do not control the runtime.
- CLI tools: when a tool has to be fast and portable, a single cross-compiled binary beats “install Python first.” For a quick one-off, I still just write shell.
- Infrastructure tooling: operators and controllers that run in hot paths and have to be reliable where I do not control the runtime.
- CLI tools: when a tool has to be fast and portable, a single cross-compiled binary beats “install Python first.” For a quick one-off, I still just write shell.
My bottom line
Go is awesome not because it does everything, but because it nails the one job I hand it: performance without operational pain.
Shell is my default and handles most of my work. Python takes over when shell gets too complex. Go is what I climb to when I am explicitly optimizing for performance — and at that tier, single static binaries that start fast, run lean, and read clearly are exactly what I want. Knowing which tier a problem belongs to is most of the skill.