Why Go is a good language for microservices

Pawan Rawal
SafetyCulture Engineering
3 min readJun 25, 2019
Go Crew at SafetyCulture

Early last year, we decided to move to Go (Golang) as the language of our choice for our microservices at SafetyCulture. Before this, our microservices were written in Node.js (a mixture of CoffeeScript, Javascript and TypeScript). Below I’ll share the reasons why we made the change.

1. Static types

Go is a statically typed language which means the compiler can do more work for you. People tend to underscore the importance of this point.

The story of a production incident

Last year, while implementing a bug fix for one of our core microservices, I caused a production incident (written in Node) because I added an extra parameter to a function and forgot to pass the correct parameter while calling the function.

// function definition
function saveDocument({id, oldDocument, newDocument}) {
}
// caller
saveDocument({
id: “xyz”,
oldDoc: “blah blah”,
newDocument: “new doc”
})

My function was expecting the parameter to be called oldDocument but I passed in oldDoc. Finally, the oldDocument should have been written to Kafka, to be used by other downstream microservices. All the tests passed and I shipped the change to production only to realise 3 days later of the unintended side effects. It took us 3 days of work by two full-time engineers to fix the issue.

Sure, you could use TypeScript and get around this issue (hopefully) but why not choose a language which assists you by catching issues at compile time? In today’s world where teams are working on 20 different microservices, there is a lot to keep in your head and having a little help from a compiler won’t hurt. I have also seen similar problems in production before working on a ROR app where I could change a string variable to an int or even an array and everything is fine till it isn’t and things break for your users. Airbnb said that 38% of bugs could be prevented by using types.

2. Readability

“Clear is better than clever”

The original authors of Go placed a lot of emphasis on readability when they were designing it. Go is an easy language to pick up and get started with and for the most part, has one idiomatic way of doing things. Contrast that with a NodeJS codebase where you can come across callbacks, promises and async/await (all different ways of doing the same thing) in the same codebase. People complain that Go is verbose but we see that as a good thing at SafetyCulture. Sure you can write complex 1-liners in Python/Node that very few people would understand but that doesn’t make for a readable codebase that others can make changes to confidently.

3. Standard library

The standard library in Go is designed well and you can get quite far by using it. Maintaining a large tree of external dependencies is challenging. Every dependency that you add to your application brings in a lot of other dependencies which need to be audited for performance and security. In some cases, it can even stop you from deploying changes to your application as was shown by the leftpad incident.

“A little copying is better than a little dependency

The authors of Go and the community want the developers to be mindful of the dependencies that their applications have.

4. Performance

Go has very good support for concurrent programming and can utilise multiple cores quite well. It’s good for heavy computation as well as things like network I/O and disk seeks. You can read more about the improvements in memory and CPU usage we achieved for one of our projects in Why we moved our GraphQL server to Go.

5. Code formatting

“gofmt’s style is no one’s favorite, yet gofmt is everyone’s favorite

We don’t spend time discussing our ESLint or Prettier settings anymore because Go is opinionated about the code formatting and has a tool (gofmt) which automatically formats code for you.

Conclusion

Go offers a range of advantages over other dynamically typed languages. At the same time, no language is perfect. For e.g. — concurrent code in Go is easy to write but hard to get right for beginners to the language. This means the code can often have data races which are hard to debug. This is something that Rust does better as it prevents data races with the tradeoff that you have to understand a more complex ownership system.

References:

https://go-proverbs.github.io/

--

--