Why Go is currently my main programming language
Reasons why I chose one of the most boring and less innovative languages from its era
Of course I'm not fluent or productive in all of these. I wouldn't even consider building something serious on most of them. But I've spent most of my career trying out new things and looking for interesting ways to build software.
From all of them, right now Go is my go to language. The objective of this essay is to explain why. Most of the time I work building API servers backed by databases and message queues. This is just my opinion based on my own experiences, don't take it too seriously. If you disagree with it, Yeah, well, that's just, like, your opinion, man.
You don't have to think too much, there's usually just one way of doings things. In other languages like C++, Clojure, Scala or even Rust, I have to make lots of micro decisions on how to actually write the code. Should I use iterators? Do I need to make a template? Should I use “Thread First" or “Thread Last”?
Go is so boring and inexpressive. I don't have to waste time trying to find the best way to do something. I just need to find out how to make that ugly blocks of
”if err != nil” readable enough.
Yeah, error handling is verbose, but it makes really obvious when function calls can fail. This forces me to write more reliable code.
Go code often looks the same don't matter who is writing it in the team. This makes it easy for anyone to contribute to the codebase, without the need of creating heavy style guides or discussing in code reviews what is the most idiomatic way to build a feature.
The code very often is easy to read. There is no much syntax to be confused about. I've been teaching Go to junior engineers straight out of college for years and they get productive fast.
I don't write code at work to feel smart, I need to get shit done.
It just works
Build times are fast! I don't use a fancy IDE, I write code on emacs and build/run it in a terminal. By saving and running
“go build“ or
“go test“, I can almost instantly get feedback from the compiler, no matter the size of the codebase.
I try as much as I can to make illegal states unrepresentable by relying heavily on the type system. Of course, the nullable pointers and lack of sum types sometimes get in the way, but I tend to fix the shortcomings of this through unit testing. Most of the time (when you are experienced enough), if the code compiles, it will work in prod.
Also, as Go binaries are statically linked, it is so easy to distribute the final build artifact. Most of my Dockerfiles look like this:
FROM golang:1.21 as builder
COPY . .
RUN make compile
COPY --from=builder /build/app /app
There are no hassles of installing numerous OS shared libraries and experiencing fucking pain while attempting to build it in another environment, as often happens with Python. Or having to ship hundreds of megabytes of the JVM every time for it to work.
The Go core team is VERY conservative on adding new syntax and changing runtime behavior. If you have learned Go five years ago, it is pretty much the same language now (except for generics), and this will probably be true five years from now.
There are not much languages out there that you can say the same about. I think Clojure is similar in this way and maybe it's why I also like it so much. Maybe some other day I'll talk about it.
I fucking hate breaking changes. I usually have the worst time working with Node.js for example. The ecosystem is so broken and fragmented. The libraries and tools you use today will be outdated in a year. The maintainers will add lots of breaking changes just for the sake of a "simpler" API and make you refactor your code with very little benefits on your side.
Also there's the runtime stability. I've been using Go in production for almost seven years. Most of the applications run for several weeks or months without babysitting.
The performance is often nice out of the box. The memory footprint is low compared to other popular programming languages. The garbage collector usually don't get in the way of acceptable latency. It is easy to add concurrency with Goroutines and the race detector often does a good job finding bugs.
The CLI is simple. It's very simple to run tests and code coverage is supported out of the box. It's easy to create benchmarks and reason about code performance. The built in profiler is very useful. There are no centralized package managers and publishing bureaucracy. It forces you to use the dependencies you declare. The code formatting comes out of the box and makes sure no one is happy about it, but is consistent. The documentation website is simple, loads fast and it's very easy to document your own code.
Not a single tool is outstanding or innovative compared to what is out there. But having all of this from day one makes the developer experience awesome! In other programming languages, to have all of these, I have to invest lots of hours choosing tools and dependencies, and they never feel so integrated.
What I don't like
Because of more than a decade lacking of generics, there are lots of codebases relying on code generation. This enables very bad developer experience in some projects.
After working with much more expressive programming languages before, it is very frustrating sometimes to write 20 lines of code for something that would take three lines in others.
The lack of sum types always makes me sad.
Go is not my favorite language and it is not the one I have more fun coding. But it is a simple stack with a great community. It powers technologies most of us use everyday and lots of relevant tech companies rely on it.
I'm always looking at new programming languages and re-checking the ones that get more mature over time. Right now, for the reasons above, Go is the language that makes my work more productive and less frustrating. Until something better comes up, I'll be still using it.