Ive recently had the perfect use case to try my hand at Golang (more on the particular project later).
It’s been a long time coming. Its no secret you will find many articles on the web proclaiming how good Golang is, and its always been something I wanted to try out.
Until now I haven’t really had a use case and I feel its always much better to have a “need” to use something to really evaluate it, or your stuck just running through hello world’s and tutorials where your doing little more than copy/pasting working code.
The project in question is almost ready for a big reveal, but for the purposes of this article lets just say I needed a binary I could distribute without the surrounding architecture needing a runtime to run the application.
So that ruled out my two go to languages, PHP and NodeJs/Javascript. And lead me down the Golang or Rust path.
I will concede I actually attempted to use Rust first. There seems to be even more hype around Rust at present and watching all the conferences around Rust and WebAssembly that are popping up on YouTube I took a stab. As this is a Golang article I won’t delve too deep into Rust, lets just say after half a day I quickly realised if I wanted to get a proof of concept out quickly for this new project Rust was not the language I should be learning on the job for. This is by far not a knock at Rust. It looks really promising, it is probably just a little too far out of my comfort level right now.
So having wasted a half a day I picked up the Golang handbook and started a project.
I had run all the samples you can find in the documentation and the tour guide multiple times in the past so the core concepts like operators, values, structs etc I had an understanding of already. Packages on the other hand caused me some confusion early on which I’ll delve into further down this article.
The Proof Of Concept
Building the POC I dumped all my code in the main
package. At this point the code was pretty ropey. But it did confirm for me the approach was going to work. I felt pretty at home with writing at least basic code, the syntax is very similar to PHP and javascript. Well to be more specific strongly typed PHP8.0 and Typescript.
I was getting on quite well with the language and now wanted to refactor the work into something I would be happy to commit to version control.
Folder Structure
This still irks me a little, it’s almost hard coded into my brain. Code goes into a src
folder and is organised by logical groupings. At least this is how I work and is the accepted structure for the languages I use day to day.
Golang on the other hand has a different recommended structure. Its not a requirement but is recommended so I went with it. Full details can be found here: https://github.com/golang-standards/project-layout
Im sure there must be a reason that’s inherent in the language or devised by some people smarter than me that makes this a better structure, but at this point I just don’t like it. It may become clearer the more I use Golang but I just feel the separation detaches the domain relationships and functionality. I don’t see why private code should be in a wholly different location when Golang has public exports inherent in the language.
Modules
This is where I hit my first real hurdle, maybe I didn’t read the docs well enough, or maybe it was just me. But I had a good few hours of frustration with my IDE (Intellij IDEA) shouting at me because it couldn’t find my code. At least part of the problem was my choice to just dump everything in main.go
, then migrating to a directory type project, and finally landing on a module structure. I also think I may of been led down a few garden paths reading up on outdated articles.
I managed to figure it out eventually and it is pretty obvious with hindsight. I haven’t yet been in a position to write a module that’s meant to be consumed externally so i’m cautious of being too critical here. But from my relatively short experience I don’t understand why you can’t use relative imports in internal code?
The fact only its name is known by a package, but it needs to explicitly define the whole module path to pickup other packages feels a little off to me:
package foobar import ( "github.com/xxx/xxx/internal/bazzer" // why not just "../bazzer"? )
Dependency Loops
Im not sure how common this is, but I had a real hard time when refactoring due to cyclic dependencies on structs. Again mainly my fault and bad design in my proof of concept. If I had started with modules up front I don’t think I would of had this issue. But the good thing that came of this is my understanding of Golang interfaces.
What I really like about them is their implicit rather than explicit nature (duck typing). When I found out about this it all became clear what I needed to do to resolve my problems.
err != nil
I don’t think a Golang article is complete without the mention of err != nil
. Even before I started I knew about this. I have seen various articles propose how to write better code to prevent the repetition. Something I did pickup along the way was the use of defer
which is a pretty neat feature allowing you to something similar to try{}finally
. But I would be lying if I didn’t say I would be immensely grateful to see try{}catch
make an appearance in Golang.
Releasing
Building the project was always super simple. I then needed to figure out how I could distribute this for different architecture. By the time I got here I was feeling pretty confident with what I had achieved, my confidence lead me to starting to reinventing a process already handled exceptionally by the Goreleaser and Godownloader projects. These two projects make it super simple to provide access to built copies of your app.
Which made it very surprising Godownloader seems to be struggling: https://github.com/goreleaser/godownloader/issues/161
To me these two packages feel like the composer and npm of the PHP and Javascript world i’m used to.
They appear to be a critical part of the ecosystem and certainly helped me get to a usable system very quickly.
In short a simple generated yml file and some CI scripts and I had automated releases available to the project without much customisation required.
Conclusion
There are definitely some things i’m not used to or fully understand. But i’m still very interested and very happy with what i’ve able to achieve pretty quickly. It has a familiar syntax which no doubt helps and I can’t believe i’ve got to the conclusion without mentioning its SUPER fast. I’ll be talking more about the specific project this was about when its ready for launch. The source code will be open source, so please don’t judge it too harshly. It will be my first Golang project, hopefully one of many.
I always enjoy reading about newcomers experiences when taking a look at Go. I’ve been writing Go for 4 years now and I can say that the stuff your running into, apart from perhaps the dependency loops are pretty common observations. Thanks for the read!
I do think one of the most worthwhile things to spend time on is interfaces. I recently did a write-up you might find useful: https://qvault.io/2020/03/15/best-practices-for-writing-clean-interfaces-in-go/
Yes the more i think about it the more i realise the dependency loops i had was just a bit of bad design on my part when hashing it all out. Thanks for reading!