Go: What We Got Right, What We Got Wrong

This page summarizes the projects mentioned and recommended in the original post on news.ycombinator.com

Our great sponsors
  • WorkOS - The modern identity platform for B2B SaaS
  • InfluxDB - Power Real-Time Data Analytics at Scale
  • SaaSHub - Software Alternatives and Reviews
  • go

    The Go programming language

  • It's not at "an accident" and Go didn't "somehow" fail to replace C++ at its systems programming domain. The reason why go failed to replace C and C++ is not a mystery to anyone: Mandatory GC and a rather heavyweight runtime.

    When the performance overhead of having a GC is less significant than the cognitive overhead of dealing with manual memory management (or the Rust borrow checker), Go was quite successful: Command line tools and network programs.

    Around the time Go was released, it was certainly touted by its creators as a "systems programming language"[1] and a "replacement for C++"[2], but re-evaluating the Go team's claims, I think they didn't quite mean in the way most of us interpreted them.

    1. The Go Team members were using "systems programming language" in a very wide sense, that include everything that is not scripting or web. I hate this defintion with passion, since it relies on nothing but pure elitism ("Systems language are languages that REAL programmers uses, unlike those "Scripting Languages"). Ironically, this usage seems to originate from John Ousterhout[3], who is himself famous for designing a scripting language (Tcl).

    Ousterhout's definition of "system programming language" is: Designed to write applications from scratch (not just "glue code"), performant, strongly typed, designed for building data structures and algorithms from scratch, often provide higher-level facilities such as objects and threads.

    Ousterhout's definition was outdated even back in 2009, when Go was released, let alone today. Some dynamic languages (such as Python with type hints or TypeScript) are more strongly typed than C or even Java (with its type erasure). Typing is optional, but so it is in Java (Object), and C (void*, casting). When we talk about the archetypical "strongly typed" language today we would refer to Haskell or Scala rather than C. Scripting languages like Python and JavaScript were already commonly used "for writing applications from scratch" back in 2009, and far from being ill-adapted for writing data structures and algorithms from scratch, Python became the most common language that universities are using for teaching data structures and algorithms! The most popular dynamic languages nowadays (Ruby, Python, JavaScript) all have objects, and 2 out of 3 (Python and Ruby) have threads (although GIL makes using threads problematic in the mainstream runtimes). The only real differentiator that remains is raw performance.

    The widely accepted definition of a "systems language" today is "a language that can be used to systems software". Systems software are either operating systems or OS-adjacent software such as device drivers, debuggers, hypervisors or even complex beasts like a web browser. The closest software that Go can claim in this category is Docker, but Docker itself is just a complex wrapper around Linux kernel features such as namespaces and cgroups. The actual containerization is done by these features which are implemented in C.

    During the first years of Go, the Go language team was confronted on golang-nuts by people who wanted to use go for writing systems software and they usually evaded directly answering these questions. When pressed, they would admit that Go is not ready for writing OS kernels, at least not now[4][5][6], but GC could be disabled if you want to[7] (of course, there isn't any way to free memory then, so it's kinda moot). Eventually, the team came to a conclusion that disabling GC is not meant for production use[8][9], but that was not apparent in the early days.

    Eventually the references for "systems language" disappeared from Go's official homepage and one team member (Andrew Gerrand) even admitted this branding was a mistake[10].

    In hindsight, I think the main "systems programming task" that Rob Pike and other members at the Go team envisioned was the main task that Google needed: writing highly concurrent server code.

    2. The Go Team members sometimes mentioned replacing C and C++, but only in the context of specific pain points that made "programming in the large" cumbersome with C++: build speed, dependency management and different programmers using different subsets. I couldn't find any claim that go was meant as a general replacement for C and C++ anywhere from the Go Team, but the media and the wider programming community generally took Go as a replacement language for C and C++.

    When you read through the lines, it becomes clear that the C++ replacement angle is more about Google than it is about Go. It seems that in 2009, Google was using C++ as the primary language for writing web servers. For the rest of the industry, Java was (and perhaps still is) the most popular language for this task, with some companies opting for dynamic languages like Python, PHP and Ruby where performance allowed.

    Go was a great fit for high-concurrency servers, especially back in 2009. Dynamic languages were slower and lacked native support for concurrency (if you put aside Lua, which never got popular for server programming for other reasons). Some of these languages had threads, but these were unworkable due to GIL. The closest thing was frameworks Twisted, but they were fully asynchronous and quite hard to use.

    Popular static languages like Java and C# were also inconvenient, but in a different way. Both of these languages were fully capable of writing high-performance servers, but they were not properly tuned for this use case by default. The common frameworks of the day (Spring, Java EE and ASP.net) introduced copious amounts of overhead, and the GC was optimized for high throughput, but it had very bad tail latency (GC pauses) and generally required large heap sizes to be efficient. Dependency management, build and deployment was also an afterthought. Java had Maven and Ivy and .Net had NuGet (in 2010) and MSBuild, but these where quite cumbersome to use. Deployment was quite messy, with different packaging methods (multiple JAR files with classpath, WAR files, EAR files) and making sure the runtime on the server is compatible with your application. Most enthusiasts and many startups just gave up on Java entirely.

    The mass migration of dynamic language programmers to Go was surprising for the Go team, but in hindsight it's pretty obvious. They were concerned about performance, but didn't feel like they had a choice: Java was just too complex and Enterprisey for them, and eeking out performance out of Java was not an task easy either. Go, on the other hand, had the simplest deployment model (a single binary), no need for fine tuning and it had a lot of built-in tooling from day one ("gofmt", "godoc", "gotest", cross compilation) and other important tools ("govet", "goprof" and "goinstall" which was later broken into "go get" and "go install") were added within one year of its initial release.

    The Go team did expect server programs to be the main use for Go and this is what they were targeting at Google. They just missed that the bulk of new servers outside of Google were being written in dynamic languages or Java.

    The other "surprising use" of Go was for writing command-line utilities. I'm not sure if the original Go team were thinking about that, but it is also quite obvious in hindsight. Go was just so much easier to distribute than any alternative available at the time. Scripting languages like Python, Ruby or Perl had great libraries for writing CLI programs, but distributing your program along with its dependencies and making sure the runtime and dependencies match what you needed was practically impossible without essentially packaging your app for every single OS and distro out there or relying on the user to be a to install the correct version of Python or Ruby and then use gem or pip to install your package. Java and .NET had slow start times due to their VM, so they were horrible candidates even if you'd solve the dependency issues. So the best solution was usually C or C++ with either the "./configure && ./make install" pattern or making a static binary - both solutions were quite horrible. Go was a winner again: it produced fully static binaries by default and had easy-to-use cross compilation out of the box. Even creating a native package for Linux distros was a lot easier, so all you add to do is package a static binary.

    [1]: https://opensource.googleblog.com/2009/11/hey-ho-lets-go.htm...

    [2]: https://web.archive.org/web/20091114043422/http://www.golang...

    [3]: https://users.ece.utexas.edu/~adnan/top/ousterhout-scripting...

    [4]: https://groups.google.com/g/golang-nuts/c/6vvOzYyDkWQ/m/3T1D...

    [5]: https://groups.google.com/g/golang-nuts/c/BO1vBge4L-o/m/lU1_...

    [6]: https://groups.google.com/g/golang-nuts/c/UgbTmOXZ_yw/m/NH0j...

    [7]: https://groups.google.com/g/golang-nuts/c/UgbTmOXZ_yw/m/M9r1...

    [8]: https://groups.google.com/g/golang-nuts/c/qKB9h_pS1p8/m/1NlO...

    [9]: https://github.com/golang/go/issues/13761#issuecomment-16772...

    [10]: https://go.dev/talks/2011/Real_World_Go.pdf (Slide #25)

  • rfcs

    RFCs for changes to Rust

  • Rust shares Go's "errors as values + panics" philosophy. Rust also has a standard library API for catching panics. Its addition was controversial, but there are two major cases that were specifically enumerated as reasons to add this API: https://github.com/rust-lang/rfcs/blob/master/text/1236-stab...

    > It is currently defined as undefined behavior to have a Rust program panic across an FFI boundary. For example if C calls into Rust and Rust panics, then this is undefined behavior. Being able to catch a panic will allow writing C APIs in Rust that do not risk aborting the process they are embedded into.

    > Abstractions like thread pools want to catch the panics of tasks being run instead of having the thread torn down (and having to spawn a new thread).

    The latter has a few other similar examples, like say, a web server that wants to protect against user code bringing the entire system down.

    That said, for various reasons, you don't see catch_unwind used in Rust very often. These are very limited cases.

  • WorkOS

    The modern identity platform for B2B SaaS. The APIs are flexible and easy-to-use, supporting authentication, user identity, and complex enterprise features like SSO and SCIM provisioning.

    WorkOS logo
  • nilaway

    Static analysis tool to detect potential nil panics in Go code

  • I would have more respect if they at least admitted to the flawed type system but instead say it is not a problem. It is disappointing to see past mistakes repeated in a new programming language. Even the Java language creator was humble enough to admit fault for the null pointer problem. The Go devs do not have such humility.

    https://github.com/uber-go/nilaway

  • evcxr

  • https://github.com/evcxr/evcxr can run Rust in a Jupyter notebook. It's not Golang but close enough.

  • gophernotes

    The Go kernel for Jupyter notebooks and nteract.

  • https://github.com/gopherdata/gophernotes

    I've had this bookmarked for some time and just havent gotten around to it.

  • Gitea

    Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD

  • llgo

    Discontinued LLVM-based compiler for Go

  • > And of course, today there is an LLVM-hosted compiler for Go, and many others, as there should be.

    Isn't that the dead llgo effort?

    https://github.com/go-llvm/llgo (now archived)

    Its readme points to a dead link on the LLVM website, and it looks like there's no matching Go project under the LLVM org.

    Does anyone know if there really is still a working LLVM based Go toolchain (other than TinyGo)?

  • InfluxDB

    Power Real-Time Data Analytics at Scale. Get real-time insights from all types of time series data with InfluxDB. Ingest, query, and analyze billions of data points in real-time with unbounded cardinality.

    InfluxDB logo
  • usbarmory

    USB armory - The open source compact secure computer

  • Niklaus Wirth, rest his soul, would disagree.

    Like would the the selling USB Armory, with Go written firmware.

    https://www.withsecure.com/en/solutions/innovative-security-...

    Back in my day, writing compilers and OS services were also systems programming.

  • gdu

    Fast disk usage analyzer with console interface written in Go

  • Not sure these are really popular, but I cannot resist advertising a few utilities written in Go that I regularly use in my daily workflow:

    - gdu: a NCDU clone, much faster on SSD mounts [1]

    - duf: a `df` clone with a nicer interface [2]

    - massren: a `vidir` clone (simpler to use but with fewer options) [3]

    - gotop: a `top` clone [4]

    - micro: a nice TUI editor [5]

    Building this kind of tools in Go makes sense, as the executables are statically compiled and are thus easy to install on remote servers.

    [1]: https://github.com/dundee/gdu

    [2]: https://github.com/muesli/duf

    [3]: https://github.com/laurent22/massren

    [4]: https://github.com/xxxserxxx/gotop

    [5]: https://github.com/zyedidia/micro

  • duf

    Disk Usage/Free Utility - a better 'df' alternative

  • Not sure these are really popular, but I cannot resist advertising a few utilities written in Go that I regularly use in my daily workflow:

    - gdu: a NCDU clone, much faster on SSD mounts [1]

    - duf: a `df` clone with a nicer interface [2]

    - massren: a `vidir` clone (simpler to use but with fewer options) [3]

    - gotop: a `top` clone [4]

    - micro: a nice TUI editor [5]

    Building this kind of tools in Go makes sense, as the executables are statically compiled and are thus easy to install on remote servers.

    [1]: https://github.com/dundee/gdu

    [2]: https://github.com/muesli/duf

    [3]: https://github.com/laurent22/massren

    [4]: https://github.com/xxxserxxx/gotop

    [5]: https://github.com/zyedidia/micro

  • massren

    massren - easily rename multiple files using your text editor

  • Not sure these are really popular, but I cannot resist advertising a few utilities written in Go that I regularly use in my daily workflow:

    - gdu: a NCDU clone, much faster on SSD mounts [1]

    - duf: a `df` clone with a nicer interface [2]

    - massren: a `vidir` clone (simpler to use but with fewer options) [3]

    - gotop: a `top` clone [4]

    - micro: a nice TUI editor [5]

    Building this kind of tools in Go makes sense, as the executables are statically compiled and are thus easy to install on remote servers.

    [1]: https://github.com/dundee/gdu

    [2]: https://github.com/muesli/duf

    [3]: https://github.com/laurent22/massren

    [4]: https://github.com/xxxserxxx/gotop

    [5]: https://github.com/zyedidia/micro

  • gotop

    A terminal based graphical activity monitor inspired by gtop and vtop (by xxxserxxx)

  • Not sure these are really popular, but I cannot resist advertising a few utilities written in Go that I regularly use in my daily workflow:

    - gdu: a NCDU clone, much faster on SSD mounts [1]

    - duf: a `df` clone with a nicer interface [2]

    - massren: a `vidir` clone (simpler to use but with fewer options) [3]

    - gotop: a `top` clone [4]

    - micro: a nice TUI editor [5]

    Building this kind of tools in Go makes sense, as the executables are statically compiled and are thus easy to install on remote servers.

    [1]: https://github.com/dundee/gdu

    [2]: https://github.com/muesli/duf

    [3]: https://github.com/laurent22/massren

    [4]: https://github.com/xxxserxxx/gotop

    [5]: https://github.com/zyedidia/micro

  • micro-editor

    A modern and intuitive terminal-based text editor

  • Not sure these are really popular, but I cannot resist advertising a few utilities written in Go that I regularly use in my daily workflow:

    - gdu: a NCDU clone, much faster on SSD mounts [1]

    - duf: a `df` clone with a nicer interface [2]

    - massren: a `vidir` clone (simpler to use but with fewer options) [3]

    - gotop: a `top` clone [4]

    - micro: a nice TUI editor [5]

    Building this kind of tools in Go makes sense, as the executables are statically compiled and are thus easy to install on remote servers.

    [1]: https://github.com/dundee/gdu

    [2]: https://github.com/muesli/duf

    [3]: https://github.com/laurent22/massren

    [4]: https://github.com/xxxserxxx/gotop

    [5]: https://github.com/zyedidia/micro

  • yaegi

    Yaegi is Another Elegant Go Interpreter

  • Yes. There are long standing feature requests for (e.g.) the reflect package that simply don't get done because they'd break this assumption and/or force further indirection in hot paths to support "no code generation at runtime, ever".

    Packages like Yaegi (that offers an interpreted Go REPL) have "know limitations, won't be addressed" also because of these assumptions.

    https://github.com/golang/go/issues/4146

    https://github.com/golang/go/issues/16522

    https://github.com/traefik/yaegi?tab=readme-ov-file#limitati...

  • SaaSHub

    SaaSHub - Software Alternatives and Reviews. SaaSHub helps you find the best software and product alternatives

    SaaSHub logo
NOTE: The number of mentions on this list indicates mentions on common posts plus user suggested alternatives. Hence, a higher number means a more popular project.

Suggest a related project

Related posts