Should we migrate from C++ to Go?
Go does a lot of things well (good performance, easy to learn, very productive, extensive stdlib, excellent tooling, etc), but after programming with Go for three years (both embedded Linux and cloud applications), stability is the characteristic that really stands out.
In the past, I’ve observed the development of several fairly large C++ applications. Stability problems were more common than they should have been. Memory leaks, null pointers, crashes, etc. On one project (over 500,000 SLOC) we built elaborate mechanisms to capture core dumps when things crashed, at times needed to run Gdb on the target system, and spent a fair amount of time in Valgrind tracking down memory leaks. The worst problems were the rare, intermittent ones which only happened in the field and were very difficult to instrument. It is possible to write reliable C/C++ code (one example is the Linux kernel), but it takes focused effort (code review, testing, coding standards, etc).
In a study of four decent-sized Go projects and stability problems are a thing of the past. I only recall one memory leak. Crashes are rare. Several attributes of Go contribute to this:
- The pattern for returning multiple variables (including error) and aligning the happy path to the left encourages error checking before doing anything with the data a function returns. Therefore you don’t end up trying to do things with nil variables. It is a little verbose, but it works and is a small price to pay for the problems it solves.
- Garbage collection takes care of memory leaks.
- The language encourages simplicity, and simple things are generally more reliable.
- The type system catches many errors during compile time.
I’m sure there are others, but these are the ones that stand out to me. Go strikes a nice balance between safety and simplicity. There are safer languages (Rust, Haskell, etc), but they require more effort to learn, and probably require more coding effort (I don’t have a lot of experience, so I could be wrong about this). Every tool has its use and place. If I was developing an application on a space craft going to Mars, I’d probably use Rust. If I am writing a quick script to process some test data, I typically use Python. When writing MCU code, I still use C/C++, but someday may use Rust. But when I’m writing a reasonably complex cloud or edge (embedded Linux) application, I use Go. In Go, you can get things done quickly, and be reasonably confident that the application you deploy will not crash or leak memory.
Go’s type system and simplicity require a little more typing than Python, but likely less effort than more complex languages like Rust or Haskell. Go is a pragmatic language that gets you 98% there without a lot of overhead. In business, you generally don’t want to add a lot of process overhead on the entire company for the 2% of people who may be causing problems, as it imposes drag on the other 98% trying to get things done. Rather, it makes sense to address the 2% at a personal level. Likewise, with Go, the 2% of the time you have to fix a nil bug, or a unhandled case the type system did not catch, is acceptable as 98% of the time you are working in a language that is very productive with very little overhead. Go is ideal for many applications.