The Gopher, There and Back Again

In Go
8 Min Read

A bit of history is customary for a first blog post, right? My background as a software developer is still mainly in PHP. I first encountered it when I was in high school working on a project with a friend of mine. We wanted to get a site set up for the project and found a pretty neat pre-packaged solution written in PHP. I didn't really understand what the code was doing at the time at all, and didn't take much time to bother trying to understand it either. Years went by and I crossed paths with PHP again. From the beginning of my time at University 'til around 6 months ago (~7 years I guess), PHP had been one of my main focuses. Over 2 years ago I was first introduced to Go.

The bulk of my professional experience had been spent at an agency based in central Leeds. It was a fantastic company to work for overall, and I learnt a huge amount from it, as I was given quite a lot of freedom very early on. I worked on a wide range of projects; standard CMS builds, bespoke APIs, hybrid mobile apps, automation for "sprint 0s", deployment and infrastructure, and many things in-between.

During my time at this agency I became friends with an intern we hired. He's a really talented guy, and he happened to be the one to introduce me to Go. I said I'd give it a try and see what I thought of it. Right away I thought it'd be something I'd love. The promise of better performance compared to PHP, type-safety, concurrency, and native binaries that are tiny and start quickly was very enticing! There are a lot of things to be excited about if you're coming from PHP to Go.

Weirdly though, I didn't actually like it that much. I thought it had a bit of a weird syntax, it's tooling was okay, handling errors was just so onerous, and how on Earth could you get around the lack of generics in a language like Go? I remember having a lot of conversations with that same intern about this, he probably told me all of the things I would come to realise myself later on, but instead I continued on the hunt for something new to learn outside of PHP, and now instead of Go too.

The Year of Scala

My search quite quickly led me to Scala. If you've not heard much about Scala it's a functional programming language that runs on the JVM. That was quite attractive as a starting point because the JVM is fast (once it's up and running), and functional programming is this hot new (not really) craze that's taken the programming world by storm.

I dove into Scala, and spent maybe a year trying to wrap my head around it's idioms. At first I was in love. Despite having longer compile times than Go, using SBT actually meant compile times weren't all that bad (thanks to incremental compilation). I learnt the syntax, and spent some time basically just writing Scala like you might write Java, but with a slightly less verbose syntax. Next I focused on learning functional programming principles like purity, referential transparency, and immutability. I'd used many of these things before without knowing it whilst writing PHP, but hadn't really thought about how much differently you'd have to write an application to make it purely functional.

As my year with Scala progressed, I'd read loads of blog posts, followed the tutelage of some pretty popular books in the Scala community, and had started Martin Odersky's course on Functional Programming Principles in Scala. I felt like I should not be in a place where I was making things that were useful. I also felt like I should be in a place where if the opportunity to work on a Scala project at work should arise, that I should be confident enough in my own skills with Scala to ask to work on it. The thing was... I didn't feel like that at all. I still felt like other people's code was extremely confusing and difficult to read. I felt like every other library had it's own completely different style and set of vocabulary to learn. I knew about libraries that were used out in the wild like cats and shapeless, but still didn't understand what they did or why you might need them.

Now, don't get me wrong here. Scala is a very impressive language, and I have a lot of respect for the people who work on and with the language. It's just not at all constrained, for better or for worse (in my case, worse). I love the idea of things like immutability, and I really liked how incredibly the compiler was at picking up on type errors. It was very rare that it dropped the ball and let through an error that could occur at runtime.

I hit a bit of a wall with Scala really, and just started to get frustrated by things. I started to try solve more problems I wanted to solve for myself but struggled for various reasons. Whether it be my lack of experience with functional programming still, or things like the JVM start-up time frustrating me. Somehow I was still discovering new bits of Scala's syntax a year into learning it. I felt like I'd gone down this path for a year but still didn't really have all that much to show for it.

Back to Go Again

After that year of Scala, I decided to give Go another go. I realised that there was no other language that ticked as many boxes for me as Go still did, and my year of Scala had only added more boxes to tick. But what actually were these boxes then?

  • Fast compilation times: although SBT's incremental compilation was good at speeding up compilation times overall during development, a clean compile still took a very long time, and even starting SBT took longer than a go build takes).
  • Fast application start-up times: I was interested in building CLI tools. One of my first libraries in both Go and Scala was something to make CLI applications. It was a pretty good test of how the languages compared for this.
  • Low memory footprint: I wasn't a big fan of how much memory the JVM used straight away. I also knew I'd want to be Dockerising the things that I'm making, and this is still a bit of a problem for the JVM today.
  • Feature-rich, consistent, but simple standard library: Scala has great collections, but you can end up relying on the wealth of Java libraries quite a lot. Some of which aren't all that pleasant to use. On top of which, when you start pulling in these dependencies, it starts to feel like you're having to download the internet to do some pretty basic stuff.
  • Compile-time type-checking: Scala was excellent at this, but it came at the cost of readability in many cases. Go does sacrifice some compile-time type-checking in some cases, but it is quite rare as often there is a way to create a solution that can still be checked at compile-time.
  • Garbage collection and type-safety: I didn't want to go super low-level, but I wanted to feel more in control of what my applications were doing than I did with PHP.
  • Concurrency primitives built-in: Coming from PHP, this was an area I really wanted to be able to explore more. I had a chance to use concurrency with Scala, but never felt like I was in as much control over it as I have since working with Go.
  • Widely-adopted code-style conventions: Coming from PHP where the PHP standards recommendations (PSR) were extremely popular, I knew I would like to be working with a language that also had code-style conventions that were used pretty much everywhere. This is something I personally feel many languages could benefit from, Scala included.

For me, Go provides all of these things and more. I took for granted a lot of the things that Go gives you right out of the box. Things that you can find elsewhere, but not always altogether in such a neat package as with Go.

Some of the things that initially irked me about Go no longer did. I felt like I finally understood the compromises that had to be made to produce a language like Go; to make sure that those boxes above could remain ticked. For the benefits that you do get, some other areas did have to suffer a little - but it was clear that the language was designed to maximise developer productivity overall. Error handling is a great example of this. It is verbose, and it can sometimes be a little unhelpful even. It is however extremely clear where an error can occur in a function body though, which means if you're reading someone else's code you're going to be able to understand when or why it might error.

Another example of one of these decisions that initially seemed quite strange to me was the context package. I wasn't a big fan of it originally, and thought that it too was a rather verbose way to handle things. Why couldn't we just kill goroutines? context is quite similar to errors though, it's verbosity is just it being explicit, and telling you that something on a given code path is likely to be able to be cancelled at some point. You can't just kill a goroutine because how would you handle any goroutines that it spawned? They're not children after all.

The conventions, and tools that have been made to help stick to them have also made Go an incredibly consistent language to work with. More so than any other language I've worked with to date. When you have tools like goimports and gometalinter, it's no surprise that pretty much every project out there has the same code style and even documentation style. The tooling is also pretty great at helping you avoid common bugs and mistakes in code.

The best part is that since I started writing software with Go, I've actually been solving problems. Real problems that I wanted to solve, not just some random tutorial math problems. More recently (and I hope to be writing about it soon), I've been making some tools to help automate some areas of my Arch Linux desktop. I've written tools to manage workspaces, or show notifications for certain things, or do things like configure my displays automatically (all of which are things that a desktop environment would solve, but it's been a blast writing this stuff).

The math problems I was advised to create solutions for when learning Scala were indeed interesting, but after having not really built any software that solved a real problem that needed solving for so long, using Go was liberating. This isn't Scala's fault, it does solve real problems. I understand it's a huge paradigm shift and I should expect a big learning curve given my history. Go simply didn't have that barrier for me; Go doesn't have much of a barrier to entry at all. There are no complex build tools, the language can entirely fit in your head, and you can get started with it very quickly. Even reading other people's code, including the code in the standard library is possible from day one.

Go isn't perfect, but it's still improving all of the time. I'm really excited about the future of the language though and seeing where it takes me along with it.


More posts in Go

The One with the Directory Structure and Manual Wiring