Context is king
Comparing programming languages is tricky and mostly an unfair game, especially if no context is supplied.
This article will focus on the contexts I have worked with in the recent years: Business software in a Microservice environment.
To answer the question how micro Micro is: I have mostly done a bit bigger services that represent the Domain-Driven Design concept of a Bounded Context in one service (or a small number of services). Carefully crafted in a way so that it does not cut a concept like a business entity - which must protect transactional and consistency boundaries - into pieces (multiple services). One example would be a Customer entity which must not be sliced into multiple services (e.g. Account, Addresses, PaymentProfiles, ...) to avoid having distributed transactions and multiple tightly coupled services which are hard to reason about.
An important benchmarking aspect, especially when looking at runtime performance, is the fact that database access or synchronous network calls to other services often diminish the pure performance of the programming language. Still this text will have a peek at pure performance figures.
With that said, this article's primary focus is to show why and when Golang can be a good choice. It will only make comparisons with other languages and ecosystems to look at the differences instead of stating Go would be a better choice per se.
Simplicity & Minimalism
Golang is a minimalist language build with simplicity in mind. It offers a limited number of concepts to build applications and this makes it simple and fast to learn. It also makes it easier to write code with low complexity and high quality, stability and readability. Developers coming from C style (OOP) languages like Java, C# or PHP are typically quickly up to speed when learning Go.
Go itself is not as expressive as other languages, which also makes it a bit harder to write expressive code. On the other hand, the more important aspects for the expressiveness of an application are e.g. structure (system architecture) or how things (variable, objects, methods) are named, to name two well-known.
Go has no inheritance, no generics, no annotation magic, no implicit type conversions but is strongly statically typed with type inference in assignments. Polymorphism is done with structurally typed interfaces through dynamic dispatch at runtime.
Packages offer clear code separation and there are only two primary visibility concepts: Exported (public) or not exported (private to the package). The only concept I'm really missing compared to other languages is a way to make types (or properties) read-only.
Tools & Ecosystem
Go comes with built-in frameworks for testing and even for profiling. There are many add-ons, especially for testing, and I almost always use some of them (see links at the bottom). My current favourite testing framework is GoConvey and I typically use Mockery to generate mocks.
With godoc you can generate documentation, gofmt and goimport (both built in) format the code and optimise the imports according to the quite strict Go code style. This strict style also limits the style discussions within teams.
What I really love when working with Go is the absence of huge, omnipotent (web) frameworks and ORMs. Those often bring lots of accidental complexity and/or can make it hard to decouple infrastructure from business logic in the sense of hexagonal architecture (aka. ports & adapters). Instead there is a number of (roughly) equally popular frameworks of reasonable complexity, each of them providing features that make them a good fit for this or that application.
Even the built in net/http package often does the job, typically combined with libraries like gorilla/mux for routing. There are also ORMs but I have no personal experience with them and I'm generally not a big fan of ORMs - no matter the programming language - for multiple reasons. But that's a story for another time.
For teams that want to apply static code analysis there is the great GolangCI-Lint library, which aggregates many linters and is highly configurable.
I could not find benchmarks that compare the compilation speed of Golang with other languages. My personal experience is that it compiles fast enough that I'm never really waiting for compilation to finish whenever I'm running tests or start a service in my local development environment without building a binary before. Again, context is king, it's a big difference if you compile a huge monolithic application or a (relatively) small (micro)service. The same is true for every other compiled language for sure.
There are tons of benchmarks out there, which compare the performance of programming languages. All of them need to be handled with care. They might be biased, or might not be very relevant in your context as described above. Still I have picked four examples with different outcomes for Go.
Go vs. Java Here Go is roughly at par.
Go vs. Python In this comparison Go wins.
Go vs. Node js Again, Go wins.
Go vs. Rust This point goes to Rust.
Some comments on these results:
Python is the only interpreted language in this comparison, hence it's slower than compiled languages by nature.
As mentioned, the pure language performance is only relevant for computation-heavy applications, not so much if the services spend most time in DB access.
Also, often it makes no sense to optimize for performance figures that are not needed for the application at hand (see: premature optimization). On the other hand, in cloud-based Microservice environments it can be a huge cost saver if you only need to run 10 instances of a service instead of 100.
Another point that is often overlooked is: How much time is needed to execute the test suite, on local developer boxes or on the CI server. We don't want our devs spending lots of their precious time waiting for test results!
With that in mind I recommend to view runtime performance as one important factor for choosing the programming language for applications, but only one of many heuristics that need to be applied to make a good decision. My main point here: Golang is definitely in good shape for excellent performance!
Concurrency is built into Go as an integral part. It is implemented by goroutines with channels to support synchronisation of goroutines. It is quite straight forward to implement patterns like producer/consumer with those two concepts. Coming from many years of PHP (which offers no concurrency concepts), I found it not very complicated to implement concurrent concepts when I started to code with Golang. Some details are a bit tricky, but that's the nature of concurrency after all.
I would suggest to have a detailed look at Golang if
- you are working on a transition from a Monolith to Microservices
- the current application, or parts of it, have real performance problems rooted in the currently used programming language(s)
- the current application needs too many servers to handle the given load and this causes very high hosting costs
- your new services don't need heavy web frameworks, just relatively simple REST or RPC endpoints
- you value the benefits of simplicity over magic, abstractions and unlimited possibilities to implement a given functionality.
Further reading recommendations & some libraries
- Why Go? – Key advantages you may have overlooked
- Awesome Go