Home Blog Links Contact

Go is a good-bad language

Date: 2025-07-02

I should preface this by stating that I’m still fairly new to Go. This is more a novice’s first impressions than the opinion of someone who knows what they’re talking about. That aside,

What do I mean by this?

I don’t really mean that Go has some nice parts and some bad parts. That’s true, but it’s also true of every language ever.

Instead, I mean that Go has features that are simultaneously good and bad.

For example, returning NULL (well, technically nil, but whatever). Returning NULL is, in my view, fundamentally kinda flawed.

You automatically lose the ability to be relatively certain the code won’t panic somewhere random, something you’d usually get with sum types and pattern matching (unless you’re doing Haskell, where forgetting a branch of a pattern match is the bane of my existence).

But, it’s convenient and readable. Yeah, Haskell has monads and Rust has the ? operator, but I always have to think for two seconds about the flow of the code with either of those. (Maybe this is a skill issue.) The if result == nil check after calling a function is more readable, at the cost of being more verbose. I don’t have to think, I know what it does.

And, in any case, in Rust, you’d probably need a match or if-let expression if the error does anything more than return back up the call stack. Your code is just as ugly. Haskell shares a similar flaw, although, if you’re creative with monads you can alleviate this somewhat.

I mean this when I call Go good-bad. It takes the easy, obvious, less-than-optimal route because fuck it. If you wanted “proper” code you’d pick another language.

Yeah, goroutines run concurrently, and you’ll occasionally have to throw some mutexes around your code, costing you performance. Theoretically, you could use a more traditional, epoll/kqueue style polling approach, which might obviate the need for those mutexes.

But, that’s incredibly annoying to do, and realistically it won’t be that much faster.

Yeah, all the files in a directory get put in the same package, without namespacing.

But hey, there’s no includes or module declarations necessary, and you probably weren’t going to have name collisions anyways.

There is a garbage collector, which isn’t optimal, but at least I don’t have to remember to free things, write the destructor correctly, or get yelled at by a strict type system.

These sorts of things are awesome from an ergonomics perspective, even if not great from a theoretical what-can-I-guarantee-about-this-code perspective.

Certainly, you can write more performant code in other languages, with stronger guarantees about runtime safety, but do you really want to? Is it going to be worth the time you’ll spend on it? Probably not.