Go annoyances
By Bjørn Borud
I really like the Go programming language. It looks a bit funny the first time you start using it, but after a while you get that “ahh, I see”-feeling whenever you discover just how convenient Go is for writing server software. But as in any language there are a few things that are really annoying.
Documentation
One thing that Java did well was Javadoc. You may or may not like the syntax or how the documentation is rendered, but it had some important features. Like being able to explicitly document parameters, return values and exceptions that might get hurled at you. When documenting methods you want this because it ties into how you should be testing and evaluating the code.
Write the method, document the parameters, return values and exceptions, along with the pre and post-conditions. Then, when you write the test, what you test against is what was promised in the documentation. You don’t think about the implementation - you only use the Javadoc. If the method does what it says on the tin, great, then you can go on to writing the tests that operate with some knowledge of what goes on inside the method. But if you can’t keep your promises, you have to go back and fix that first.
This is a good practice. Some portion of the time, this will reveal that what you promise isn’t always what you deliver. It is particularly easy to mess up what happens when you return or what happens if you feed a method unusual inputs.
There are different schools of thought about how thoroughly you should document and test code. My goal for this is simple: given the documentation and the tests you should be able to re-create the code under test.
Think of the documentation and tests as a cast of your code. You should be able to delete and re-create the code with only the docs and the tests.
Having markup that clearly identifies parameters, return values etc means you can automate checking that you have indeed documented everything you should document. The tooling can point out where you have been lazy.
I’m not fond of Go documentation conventions. When you are used to letting the tooling help you see where you have been sloppy, Go documentation comments are a big step back. You don’t have any syntax for specifying parameters and return values. And the requirements for comments are pretty much useless: comments have to begin with the function name and have at least one more word after the name. Yeah, because that ensures sensible comments, right?
You should be documenting the parameters, but there really isn’t any good way to programmatically find parameter documentation in the comment with any precision. Which means there is no good way to render it in HTML or other formats. As a consumer of documentation you end up scanning blobs of text for parameters and return values while having to glance down at the function signature.
Since there is no good way to document parameters developers tend to get sloppy about this. Myself included. Reading documentation carries a greater cognitive overhead which is paid by everyone who is going to look something up and who could benefit from precision. I often find myself having to read the code because the documentation is lacking in precision. Java actually got this right. Go didn’t.
Side note: linters
The maintainers of the original Go linter tended to be a bit on the unhelpful side - insisting that valid practices that were in widespread use should produce linter warnings. For instance when the linter complains about public functions that return unexported structs. In some cases there are valid reasons to do so and it should be possible to turn off the warning for that. But any requests to do that were just rejected.
One of the more nonsensical suggestions I remember seeing was “well, then just ignore that the linter complains about this”.
That’s not how good tooling works. If you have to allow builds that produce warnings, you will soon drown in warnings and you won’t see the ones that you actually care about. Remember the output from a typical Maven build in Java? It was so noisy you’d never spot a new warning turning up. So warnings became entirely useless.
The result of not listening to developers? Rather than have one linter that everyone uses, now we have several. Which have to be installed, understood and managed. And while you may shrug and say this is a good thing, it would have been more useful to have a linter 99% of developers can agree on and which can be part of the standard tooling. It unnecessarily fragments how Go is used.
Sometimes being able to compromise leads to better outcomes than being pig-headed and short-sighted.
Logging
And while we are on the topic of fragmentation, Go could also have learned from Java when it comes to logging. Not because Java did everything right, but because Java did a lot of things wrong that we can learn from.
When logging became part of the standard library in Java 1.4 there already existed a fauna of logging frameworks. Some of them rather beastly things that were big, clumsy and slow. But what most programmers focused on was the bits of these tools that rendered log messages onto disk, network or console. People chose frameworks based on aesthetics - not technical soundness.
The Java logging landscape ended up as an unmitigated disaster. With a lot of work going into trying to abstract your way out of the mess. Yes, java.util.logging
did have weaknesses. Stack trace generation used to be slow, but that quickly stopped being a problem. And it should have had an API that allowed you to postpone interpolation to when you know if the log message will ever be output. But it was minimal, easy to understand and very easy to use and it was a better starting point for evolving something that could have ticked the boxes of most developers.
So surely Go learned something from this? No. Go seems to have learnt nothing. The built-in logging is about as primitive as it gets. It doesn’t even have the concept of log levels. Let alone any useful structure. Why couldn’t the designers of this have spent a day or two talking to an experienced Java programmer and try to learn something about logging before embarking on the log
package? The knowledge existed. They just didn’t tap into it.
The result? We now have lots of different logging frameworks, the abstractions aren’t very good, there is no one way to do logging in Go, and the problem seems to get a little bit worse every year.
This was entirely unnecessary and thoughtless.
Time formats
Most languages have some variant of the syntax of strftime(3)
. Most programmers know how this syntax works. By heart. You don’t have to look it up. Why on earth do you then have to invent a new syntax for time formats that requires you to memorize a “magic” timestamp, and try to remember if it follows the broken US practice of illogical month/day ordering. It has one redeeming feature though: it is easier to specify how many digits you want in fractional seconds. But this wasn’t a problem in need of fixing. And the solution didn’t make the cognitive overhead any lower.