Go: What we got right, what we got wrong
A retrospective by Go co-creator Rob Pike has reignited debate over what the language got right and wrong in its design and evolution. Commenters broadly credit Go with fast tooling, a strong standard library, simple concurrency primitives, and a deliberately small, stable core that made it a popular choice for servers and infrastructure. Criticisms focus on weak FFI and scientific‑computing support, the long delay and current limits of generics, pervasive nil/error pitfalls, package and versioning missteps, and a leadership style some see as dismissive of community needs, especially around error handling, REPLs, and time/crypto APIs.
Go’s intended niche and what it became
- Designed at Google as a simpler, faster replacement for C++ for servers and “systems software” (in a broad sense: servers, networking, infrastructure), not for kernels or drivers.
- Several commenters say Go ended up more as a “server / internet backend” and CLI language than a low‑level systems language.
- Some argue it successfully displaced a lot of C++ and some Python in server/infrastructure work; others say it competes more with Java/C# than with C/C++/Rust.
Scientific computing, FFI, and HPC
- Early Go team reportedly dismissed scientific/HPC use cases, REPLs, and scripting‑language integration; this is seen as a missed opportunity.
- cgo and the gc compiler’s C call overhead are widely criticized; Go is viewed as having a weak FFI story, especially for scientific computing where C/Fortran interop is crucial.
- Counterpoint: most scientific work is scripting in Python/R/Julia; Go, as a systems language, is not a natural fit anyway.
Concurrency and goroutines
- Goroutines are described as user‑space “green threads” or M:N scheduling with stackful fibers on top of OS threads; good for high‑concurrency servers.
- Some think Go oversold goroutines as novel; others see them as a practical, successful model despite being similar to older green‑thread systems.
- This concurrency model complicates C FFI and influences runtime design (e.g., no code generation at runtime).
Error handling, panics, and nil
- Error‑as‑values plus panics is one of the most contested areas:
- Critics: verbose, repetitive
if err != nilpatterns, easy to accidentally swallow errors, two parallel mechanisms (errors vs panics) that don’t mesh cleanly, and painful nil handling (including interface vs concrete nil gotchas). - Defenders: explicit errors are simple and predictable, encourage handling, and avoid hidden control flow.
- Critics: verbose, repetitive
- Nil pointers and lack of null‑safety are cited as major design mistakes; some tools (e.g., linters, nil analyzers) try to mitigate this.
Generics and type system
- The long delay before adding generics is heavily debated:
- Pro: waiting avoided flawed designs that would have been hard to fix later.
- Con: launching without generics repeated old mistakes (e.g., Java pre‑generics), warped APIs, and forced workarounds (
interface{}everywhere).
- Current generics are seen as useful but incomplete (e.g., missing method‑level generics), and constrained by existing interface semantics.
Modules, packaging, and tooling
- Early GOPATH/
go getbehavior and import‑by‑URL are criticized as naïve and overfitted to Google’s monorepo; community package managers grew in the gap. - Official modules and MVS are generally praised as a big improvement, though SIV (
v2+), private repos, and SSH/git config remain pain points. - gofmt, unified tooling (
go build/test/fmt), static binaries, and relatively fast compilation are widely seen as major strengths.
Community process and evolution
- Some see Go leadership as too dismissive/slow on issues like generics, monotonic time, crypto updates, REPLs, and context usage.
- Others praise the willingness to say “no”, keep the language small, and prioritize stability and tooling over rapid feature accretion.