One of the most controversial issues in the Go language is how to handle errors. I remember when I started working with the language, in mid-2015, after having used PHP for a few years, and I was surprised that it didn’t use the famous try/catch.
After the first impact passed, I tried to understand the reason, and the official answer is that “In Go, mistakes are first-class citizens”. You are responsible for handling them explicitly.
I will try to show an example to illustrate this. I recently came across the following code in Java:
I got to this code after finding some “Error on retrieve data” occurrences in the application logs.
What’s the problem with the code above? It is impossible to know which part is causing the error, looking only at the code. It can be in SimpleHttp.doGet, model.get, Integer.parseInt, etc.
It took a refactoring so that we could make the code more robust. It looks like this +- (we can do an extra refactoring, but for this example, it’s enough):
A lot better because now we have identified that the error was happening when converting the TIMEOUT_CONSUMER environment variable to an integer since it was empty. A point of attention concerning this new code is that now the tryParseInt function knows and handles the NumberFormatException. This generated a coupling between our function and the Integer class. If, at some point, the name of this exception changes, or if a new one starts, it is possible that we need to change our code to adapt to this change.
But what does this have to do with Go? The problem presented here is not unique to Java or even the concept of try/catch. It’s not a problem caused for the person who wrote the code or reviewed it (by the way, I was one of the people who reviewed this code and let this improvement slide). My point is that Java (or another language) allows this construct.
Meanwhile, being a strongly opinionated language, Go makes this situation difficult to occur. This is my interpretation of the code above in Go:
You could argue that the code has gotten a lot more verbose. More lines to write the same functionality, and “OMG, that bunch of if/else!!!”. I agree with that argument. Indeed, the code became longer, but its reading became much more obvious. The fact that each function always returns an error forces whoever is writing the code to handle or ignore it, which would be very visible. For example, it would be possible to change the snippet:
But in that case, anyone could, in the code review, make the observation: “Hey, are you just ignoring the conversion error? Are you sure it’s okay to do this?” And as we saw in this example, conversion errors can happen ?
Here is my account of why I like how Go handles its errors. It’s far from perfect, as other languages do it differently, it’s verbose. But I’d rather write more lines and have code easier to maintain in the future.
*The content of this article is the author’s responsibility and does not necessarily reflect the opinion of iMasters.
Leave a comment