Developers love to write code. However it shouldn't be their priority! Developers should solve client's problems first and foremost, then write code if necessary. Similarly, while writing code, we shouldn't go too far with it, write just enough to solve your problem. Unnecessary code is a pain to maintain. Tons of unneeded abstraction are created every day for no real benefit. There is no need for yet another level of indirection that will make your future self struggle, while writing that next killer feature.
I witnessed the opposite while working with enterprise Java for many years: configuration XML files, bloated frameworks, annotation magic, more XML, workarounds for annotation magic, effective lack of ability to unit test stuff properly, due to framework intricacies. It took a toll on me. I thought it's the way it should be, but eventually learned it's not necessary. My last Java project used a set of small libraries that solved my problems well, yet the general impression among my peers is that many, especially older, Java projects are hard to maintain. In other words: code is not simple enough to understand and too much is happening implicitly. Lesson learned: code should be simple and explicit.
These days I'm using Go on a daily basis and I like how this language supports me on my journey to write simple and explicit code. I feel like I regained control and I'm able to solve my problems the way I want them to be solved.
if err != nil { return err }
"Error handling in Go is tedious and boring" is a common accusation. It may be for some, but it's definitely explicit like it should be. It's interesting that errors in Go are values, not exceptions, exactly like in functional programming languages that seem to get the mainstream spotlight (rightfully so!). What's more, I'd argue, that overusing the exact construct from the subtitle above is a code smell. It could mean your functions are too shallow and the design of a whole functionality could be improved.
"import cycle not allowed"
Disallowing cycles makes your code more tidy. Forces you to think about packages in a new way. Proper Go packaging groups functions that deal with certain domain together. This enables better design decisions and leads to highly cohesive packages.
implicit interfaces
What makes highly cohesive packages loosely coupled, which is a very desirable property, are implicit interfaces. Coming from another language it is very easy to misuse them, almost everyone falls into this trap, myself included. If you still use Go interfaces in similar fashion as in Java or PHP, go study some sources explaining the differences between implicit and explicit interfaces and discover their true power.
functional programming bits
Having some Clojure and Scala exposure in the past, I've learnt to appreciate and use more functional style of programming. Go may not have generics, but it's frequently easy enough to abstract stuff away by passing functions around and use currying-style dependency injection. Preferring pure functions over class-like constructs improves testability and helps to avoid overusing mocks. Achieving immutability isn't hard with the use of value receivers. Actually passing pointers around without a very good reason for it is a bad idea. So many things may go wrong when you allow to mutate a shared state.
Above features of Go is what I consider my fundamental toolbox for developing simple and reliable code. Now the real fun starts. I specify my expectations in tests and iterate on implementation. Going back and forth trying to nail down what is the simplest form I can express the requirements in. Fixing and improving existing stuff as I add new bits. Renaming things constantly in search for that perfect name that express the intent. When I cannot simplify, delete any line or rename any function or variable, I'm done. It requires discipline, but end result is always satisfying and well worth it.
They say Go is a simple language and may miss a feature or two. I disagree. It combines enough features to enable you to create powerful apps with maintainable code (and I didn't even mention channels and goroutines yet). It may force a thing or two on you, but it's actually a good thing. Embrace Go's simplicity and use it to your advantage!
Top comments (2)
Super well-written and informative, great job!
That's kind. Thanks!