Go and the case for boring
A language that ships small releases, breaks nothing, and refuses features. Its detractors and its users are describing the same thing.
Go's release notes are the least exciting in the industry, and its adoption in infrastructure software is close to total. Those two facts are the same fact.
the design decision that defines it#
Go's compatibility promise is stronger than almost any language's: code written against Go 1 continues to compile and run. That has held since 2012.
Combined with a deliberate resistance to adding features, the effect is that Go code from 2015 reads like Go code from today. There is one way to do most things. There is no dialect. There is no "modern Go" versus "legacy Go."
For a language used to write infrastructure that must be maintained for a decade by rotating teams, that property is worth more than any individual feature.
what the criticism gets right#
Error handling is verbose. if err != nil is a large share of the lines in a lot of Go code. The proposals to fix it have all been rejected, repeatedly, and the reasoning — that explicit error handling at every call site is a feature, not a bug — is coherent and is genuinely annoying to live with.
Generics arrived late and are limited. No sum types. No method type parameters. The type inference gives up in cases where you expect it to work. Generics in Go are useful and they are clearly a retrofit.
The nil interface trap. A nil pointer stored in an interface produces a non-nil interface. This bites everyone once and it is a genuine wart.
No sum types. The single most requested feature. Modeling "this is one of three things" in Go requires an interface with unexported methods and a type switch, which is neither exhaustive-checked nor pleasant.
what the criticism gets wrong#
The complaints are almost all about expressiveness, and expressiveness is not the thing Go optimizes for. It optimizes for a large team maintaining a large codebase over a long time, with new people joining constantly.
Under that objective function:
- Verbosity is fine if it makes the code obvious without context.
- Few features is good, because every feature is a dialect someone will write in and someone else will have to read.
- Fast compilation matters enormously, because it determines the feedback loop and therefore how people work.
- A good standard library means fewer dependencies, which means fewer supply chain problems and fewer upgrade treadmills.
gofmtended formatting arguments permanently, which saved the industry an incalculable number of hours.
Go is not trying to be a good language for writing clever code. It is trying to be a good language for reading code someone else wrote three years ago while you are on call at 3 a.m.
It is very good at that.
the concurrency story, honestly#
Goroutines and channels were revolutionary in 2012 and are now table stakes. Several languages have equivalent or better models.
What has held up: the scheduler is excellent, the tooling around concurrency — the race detector especially — is best in class, and context for cancellation propagation is a good design that most ecosystems lack an equivalent of.
What has not: channels are overused by people who learned Go from the tour. Most Go code should use a mutex and a plain function call. "Do not communicate by sharing memory; share memory by communicating" is good advice that has produced a lot of unnecessarily channel-based code.
where it wins#
Network services, CLI tools, infrastructure daemons, anything deployed as a single static binary, anything with a large team.
The ecosystem effect is real: a very large fraction of the cloud-native stack is Go. If you work in infrastructure, you will read Go whether or not you write it.
where it does not#
Anything needing precise memory control. Anything numeric or scientific, where the ecosystem is thin and the language does not help. Anything where expressiveness genuinely pays — a compiler, a complex domain model, anything with rich algebraic structure.
the thing worth stealing#
Whatever language you use: Go's compatibility promise, its formatting standardization, and its resistance to feature accumulation are choices, not accidents.
Most projects would benefit from adopting the spirit of all three. Pick one way to do things. Automate the formatting argument out of existence. Say no to the feature that adds a second way to express something that already has one.
That is available to you regardless of your language, and it is most of what makes Go codebases pleasant.
— Dom, February 4, 2026