My experience with Golang


By Alex Bell

Worked in IT for over 10 years, still loving it and find fascination in everything. In my day to day role I'm leading teams and often far away from the code but in my spare time I can't help myself. There's no better joy than tinkering away in the evening in what counts as the modern shed - my office.

One day, I thought, through the natural churn of projects I would eventually get a chance to work with Go; no need to do anything, ease into Jelly fish mode, sit back and wait. Zoom forward ten years and there’s barely been a blip on my Godar, just empty silence. I’m not sure what the trigger was but my patience finally gave out and I forced the situation – I would do something in Go.

Now is the Time

Fairly regularly I have a problem where I need to build a thing. The tools vary and I learned early-on that solving the problem takes precedence over refracting to accommodate the fourth iteration of a naming convention or some other such irrelevance, so it’s 'whatever gets the job done' and move on. On this occasion I have a project which requires coding but nothing about it screams a particular language - on that basis Go is as good a candidate as any.

My problem is simple - I need to move to a new house and want to pay a sensible price but the Scottish property market (where I live) is a bit odd in that the price you pay is higher than the asking price. This percentage difference I can already get at a fairly high level of aggregation but nothing as granular as I’d need to make, deep sigh, data-driven decisions.

So I had my Go project. I’d get to do some basic integration, API calls and data matching so it felt perfect to try something new in a meaningful way.

I started with a simple bit of data collection which has been going on quietly now for about six months, I get email alerts when properties enter the market and roughly three months after they’ve sold the land registry publishes the actual sold price.

I’ll leave out the detailed requirements as they’re mostly in line with what you’d expect and don’t add anything to a discussion of Go. The more subjective element is around Go itself so I’ll keep it simple and say it’s about the ability to learn, the language itself and library/framework and community support.

Now is the Time

Fairly regularly I have a problem where I need to build a thing. The tools vary and I learned early-on that solving the problem takes precedence over refracting to accommodate the fourth iteration of a naming convention or some other such irrelevance, so it’s 'whatever gets the job done' and move on. On this occasion I have a project which requires coding but nothing about it screams a particular language - on that basis Go is as good a candidate as any.

My problem is simple, I need to move to a new house and want to pay a sensible price but the Scottish property Market (where I live) is a bit odd in that the price you pay is higher than the asking price. This percentage difference I can already get at a fairly high level of aggregation but nothing as granular as I’d need to make, deep sigh, data-driven decisions.

So I had my Go project. I’d get to do some basic integration, API calls and data matching so it felt perfect to try something new in a meaningful way.

I started with a simple bit of data collection which has been going on quietly now for about six months, I get email alerts when properties enter the market and roughly three months after they’ve sold the land registry publishes the actual sold price.

I’ll leave out the detailed requirements as they’re mostly in line with what you’d expect and don’t add anything to a discussion of Go. The more subjective element is around Go itself so I’ll keep it simple and say it’s about the ability to learn, the language itself and library/framework and community support.

The Walk to the Summit

Books, YouTube, Blogs, home brew tutorials are all valid places to learn but for real bonus points spoon feeding is my preferred approach. On this front Go kinda nails it, a web based step-by-step guide with live code examples some of which form part of mini exercises. Nothing to install, open up the browser and you’re ready to go.

They’ve pitched it about the right level too. The basic assumption is that the reader is not new to programming but knows nothing about Go. I’m an experienced coder and have functional, object oriented, declarative and various DSLs under my belt so I can’t speak for how well suited it is to those with less experience. My gut feel is that as long as you’ve got some coding experience, you’d probably be fine.

The guide is split into nice bite size chunks and follows, what seemed to me at least, a very logical introduction to the language. I also like how ‘to the point’ it was - no long descriptions about the philosophical background of feature X or Y, just a nice concise here’s what it does and here’s how you use it.

I spent about two weeks, using stolen snippets of time on the train or in the evening, to get through it which is probably a day’s real effort. I found myself occasionally going off-piste and reading around stuff outside the tutorial but that was mainly to satisfy my own curiosity, everything I needed was right there.

Gotta give credit where it’s due – ten out of ten on the learning front for Go. This might seem like a trivial thing but the bit between starting to learn a language and starting to write code is a total waste so making it easy and quick is a really good sign. In a real world setting it can also be a major hurdle to adopting a new language.

The Walk to the Summit

Books, YouTube, Blogs, home brew tutorials are all valid places to learn but for real bonus points spoon feeding is my preferred approach. On this front Go kinda nails it, a web based step-by-step guide with live code examples some of which form part of mini exercises. Nothing to install, open up the browser and you’re ready to go.

They’ve pitched it about the right level too. The basic assumption is that the reader is not new to programming but knows nothing about Go. I’m an experienced coder and have functional, object oriented, declarative and various DSLs under my belt so I can’t speak for how well suited it is to those with less experience. My gut feel is that as long as you’ve got some coding experience, you’d probably be fine.

The guide is split into nice bite size chunks and follows, what seemed to me at least, a very logical introduction to the language. I also like how ‘to the point’ it was - no long descriptions about the philosophical background of feature X or Y, just a nice concise here’s what it does and here’s how you use it.

I spent about two weeks, using stolen snippets of time on the train or in the evening, to get through it which is probably a day’s real effort. I found myself occasionally going off-piste and reading around stuff outside the tutorial but that was mainly to satisfy my own curiosity, everything I needed was right there.

Gotta give credit where it’s due – ten out of ten on the learning front for Go. This might seem like a trivial thing but the bit between starting to learn a language and starting to write code is a total waste so making it easy and quick is a really good sign. In a real world setting it can also be a major hurdle to adopting a new language.

What sort of language is it?

C-ish but super clean, I’ll pick up that theme later as there is one stand out feature that it’s worth focussing on. Go has a beautiful approach to concurrency, there’s nothing super clever about it but nonetheless it’s still an elegant approach.

Concurrency's all about running things in parallel, flow control and synchronisation. Languages that support threading and locking are the basic ingredients for all of this to happen but it’s hard work, with subtle and hard to triage issues just waiting in the wings.

Concurrency in Go is based around Channels and Go Routines. Channels are how you move data and manage flow control; they behave like a thread safe FIFO Queue. Go Routines let you run things in parallel - threads but abstracted away to a degree. Nothing apparently ground-breaking. Indeed it would be easy to recreate this as a pattern or library in most languages however Go has gone 'all-in' across the language and it neatly solves a number of problems, feels intuitive and makes concurrency such low hanging fruit that it’s almost the path of least resistance.

I started without any concurrency, quite deliberately, as I wanted to understand how you would introduce it. As I made the conversion the code became simpler, easier to read and a number of small problems sort of solved themselves.

Walking this through with my project I had some major activities which happen in sequence:

  1. Check email, parse properties from content
  2. Geocode the address i.e. turn a line of text in the email in to a real location
  3. Look up by postcode for property sold prices - the API I used is rate limited
  4. Make a best guess for which property returned matches
  5. Write results to database
  6. Periodically check if the property has sold, then repeat steps 3-5.

Focusing on item one, nice and simple we login using IMAP, search for the notification emails, parse out the relevant content and then we can either call another function to do the rest or we can build a list and return than. List feels truer to a functional approach, adds to readability around sequencing of further steps, increases decoupling and is more flexible for refactoring. So that’s what I did.

Here’s where some of the path of least resistance kicks in. If I’m building an array and a channel is basically just a special array why not just output to a channel? Again, that’s what I did. I can carry on with a functional approach and once 1 returns I then do 2… but that would be missing a trick. It would make more sense to keep this channel thing going and have item 2 just reading from that channel, and may as well make it run in parallel as a Go routine, whilst I'm about it. This is a three character change to the code, add ‘go …’ to the start of the line. So that’s what I did.

Figure 1 - Adding channel

Figure 2 - Original non-channel code

Figure 3 - Now with channels

I am now in a very interesting place, in a series of small, fairly obvious low effort steps I’ve moved from thinking and coding functionally to thinking about Queues and consumers. This approach can be very powerful, but it’s also usually fairly involved - things like Camel or Spring Integration are neither obvious nor low effort.

A couple of things come for free with this approach. Firstly, I get parallelism, without any headaches around locking data access as that’s what the channel does for me but there’s one more subtle implication around flow control. Reading or writing to a channel is a blocking operation in Go. If my channel is full I can’t write to it until it’s got some space. If I’m a consumer and there’s nothing in the channel I wait until it’s got something to be read. Now before you feel nervous about that, it’s not forced on you and Go (fairly) neatly handles the opposite scenario where it’s not needed.

Usually this sort of ‘slow down’/back flow control would be real effort but it’s just there by design in Go thanks to its blocking approach. I can control the overall scaling behaviour with just two levers – size of the channel, which controls how ‘far ahead’ a producer can get, and number of Go consumers for a channel, which will control how much consumption can happen in parallel. I hope this helps highlight that a simple scenario quite quickly and intuitively leads to a place of fairly feature rich concurrency.

Go bakes this concept deeply across the board. Let’s take another simple example, in step 3 there is a rate limited API to lookup the achieved price of a house, given its postcode.

There are many ways to hand crank a solution but let’s walk it through how I believe Go intends it to be solved:

I’ve got a channel which the API calling function consumes from and there’s nothing for rate limiting right now. Go offers a timing function which is a bit like a ticker, it ticks periodically and puts that tick in a channel. To add rate limiting all I need is to setup the ticker, add a façade function which is driven by messages on this tick channel then read from the current API consumer channel and place that on new channel for the API function to read from. Simple and elegant.

Figure 4 - Original code, no rate limiting

Figure 5 - Added rate limiting

I really can’t oversell how powerful I found Go's implementation of concurrency, nor how refreshing an approach it felt even though it’s nothing new (after all enterprise integration patterns was written in 2003). Nonetheless this is the USP and single reason why Go stands apart for me from other languages.

What sort of language is it?

C-ish but super clean, I’ll pick up that theme later as there is one stand out feature that it’s worth focussing on. Go has a beautiful approach to concurrency, there’s nothing super clever about it but nonetheless it’s still an elegant approach.

Concurrency's all about running things in parallel, flow control and synchronisation. Languages that support threading and locking are the basic ingredients for all of this to happen but it’s hard work, with subtle and hard to triage issues just waiting in the wings.

Concurrency in Go is based around Channels and Go Routines. Channels are how you move data and manage flow control; they behave like a thread safe FIFO Queue. Go Routines let you run things in parallel - threads but abstracted away to a degree. Nothing apparently ground-breaking. Indeed it would be easy to recreate this as a pattern or library in most languages however Go has gone 'all-in' across the language and it neatly solves a number of problems, feels intuitive and makes concurrency such low hanging fruit that it’s almost the path of least resistance.

I started without any concurrency, quite deliberately, as I wanted to understand how you would introduce it. As I made the conversion the code became simpler, easier to read and a number of small problems sort of solved themselves.

Walking this through with my project I had some major activities which happen in sequence:

  1. Check email, parse properties from content
  2. Geocode the address i.e. turn a line of text in the email in to a real location
  3. Look up by postcode for property sold prices - the API I used is rate limited
  4. Make a best guess for which property returned matches
  5. Write results to database
  6. Periodically check if the property has sold, then repeat steps 3-5.

Focusing on item one, nice and simple we login using IMAP, search for the notification emails, parse out the relevant content and then we can either call another function to do the rest or we can build a list and return than. List feels truer to a functional approach, adds to readability around sequencing of further steps, increases decoupling and is more flexible for refactoring. So that’s what I did.

Here’s where some of the path of least resistance kicks in. If I’m building an array and a channel is basically just a special array why not just output to a channel? Again, that’s what I did. I can carry on with a functional approach and once 1 returns I then do 2… but that would be missing a trick. It would make more sense to keep this channel thing going and have item 2 just reading from that channel, and may as well make it run in parallel as a Go routine, whilst I'm about it. This is a three character change to the code, add ‘go …’ to the start of the line. So that’s what I did.

Figure 1 - Adding channel

Figure 2 - Original non-channel code

Figure 3 - Now with channels

I am now in a very interesting place, in a series of small, fairly obvious low effort steps I’ve moved from thinking and coding functionally to thinking about Queues and consumers. This approach can be very powerful, but it’s also usually fairly involved - things like Camel or Spring Integration are neither obvious nor low effort.

A couple of things come for free with this approach. Firstly, I get parallelism, without any headaches around locking data access as that’s what the channel does for me but there’s one more subtle implication around flow control. Reading or writing to a channel is a blocking operation in Go. If my channel is full I can’t write to it until it’s got some space. If I’m a consumer and there’s nothing in the channel I wait until it’s got something to be read. Now before you feel nervous about that, it’s not forced on you and Go (fairly) neatly handles the opposite scenario where it’s not needed.

Usually this sort of ‘slow down’/back flow control would be real effort but it’s just there by design in Go thanks to its blocking approach. I can control the overall scaling behaviour with just two levers – size of the channel, which controls how ‘far ahead’ a producer can get, and number of Go consumers for a channel, which will control how much consumption can happen in parallel. I hope this helps highlight that a simple scenario quite quickly and intuitively leads to a place of fairly feature rich concurrency.

Go bakes this concept deeply across the board. Let’s take another simple example, in step 3 there is a rate limited API to lookup the achieved price of a house, given its postcode.

There are many ways to hand crank a solution but let’s walk it through how I believe Go intends it to be solved:

I’ve got a channel which the API calling function consumes from and there’s nothing for rate limiting right now. Go offers a timing function which is a bit like a ticker, it ticks periodically and puts that tick in a channel. To add rate limiting all I need is to setup the ticker, add a façade function which is driven by messages on this tick channel then read from the current API consumer channel and place that on new channel for the API function to read from. Simple and elegant.

Figure 4 - Original code, no rate limiting

Figure 5 - Added rate limiting

I really can’t oversell how powerful I found Go's implementation of concurrency, nor how refreshing an approach it felt even though it’s nothing new (after all enterprise integration patterns was written in 2003). Nonetheless this is the USP and single reason why Go stands apart for me from other languages.

C-ish but super clean

Go is C-ish in the sense that it’s a strongly typed, functional language, with support for pointers (but no arithmetic) that compiles to native code. In practice however, it feels like a more dynamic language something akin to Javascript but more civilised and grown up.

It feels dynamic because whilst it’s on a pretty firm footing when it comes to concrete types it’s a flexible as possible everywhere else. For example, types can be inferred in lots of cases which at least psychologically makes it feel freer (even if it is just the compiler working a bit harder).

I also like their choice to make Interfaces duck typed. If something has the same signature as a given interface then it is that interface. To be frank it wasn’t until I saw this is in Go I realised how “strongly” typed interfaces are a bit pointless anyway. This also goes some way for making up for Go's lack of generics, or support for a ‘void’ type, with interfaces taking their place.

Cleanliness is hard to get across without writing some code. A description of every feature would be boring and not very revealing. It’s the sum of each of the small choices they made coming together as a greater whole that makes it clean. Go has removed verbosity wherever possible, adopted convention over syntax and implicitness being allowed where it makes sense.

It’s as if they started with some immovable requirements around strong typing and concurrency and were then super lenient on everything else. This has led to some, I think well chosen, features to be dropped. For example whilst Go does support pointers it doesn’t support arithmetic. Perhaps it’s more honest to say it supports passing by reference, particularly as anything more would open up a can of worms around memory management which is, in keeping with most modern languages, largely left to a black box called ‘Garbage Collection’. After all, unless you want to be truly profligate with memory, passing by reference is required to support the prime directive around concurrency, , but pointer arithmetic is not.

C-ish but super clean

Go is C-ish in the sense that it’s a strongly typed, functional language, with support for pointers (but no arithmetic) that compiles to native code. In practice however, it feels like a more dynamic language something akin to Javascript but more civilised and grown up.

It feels dynamic because whilst it’s on a pretty firm footing when it comes to concrete types it’s a flexible as possible everywhere else. For example, types can be inferred in lots of cases which at least psychologically makes it feel freer (even if it is just the compiler working a bit harder).

I also like their choice to make Interfaces duck typed. If something has the same signature as a given interface then it is that interface. To be frank it wasn’t until I saw this is in Go I realised how “strongly” typed interfaces are a bit pointless anyway. This also goes some way for making up for Go's lack of generics, or support for a ‘void’ type, with interfaces taking their place.

Cleanliness is hard to get across without writing some code. A description of every feature would be boring and not very revealing. It’s the sum of each of the small choices they made coming together as a greater whole that makes it clean. Go has removed verbosity wherever possible, adopted convention over syntax and implicitness being allowed where it makes sense.

It’s as if they started with some immovable requirements around strong typing and concurrency and were then super lenient on everything else. This has led to some, I think well chosen, features to be dropped. For example whilst Go does support pointers it doesn’t support arithmetic. Perhaps it’s more honest to say it supports passing by reference, particularly as anything more would open up a can of worms around memory management which is, in keeping with most modern languages, largely left to a black box called ‘Garbage Collection’. After all, unless you want to be truly profligate with memory, passing by reference is required to support the prime directive around concurrency, , but pointer arithmetic is not.

Opinionated

I’ve seen the word ‘opinionated’ mentioned many times about Go. I think it’s an unhelpfully loaded term. I would describe it as ‘unambiguous’; it works this way, that’s how it works. I liked this clarity, even if I didn’t always agree with all of their choices.

I started my code without Pointers, purely to get a flavour of how switching between reference and value would be. It was irritating. Go has no unreferenced variables, so every declaration gives a you a blank block of memory for whatever the type is, yet pointers can be null. This leads to some obviously cumbersome scenarios. I don’t want to over play this point, it’s really just an off-the-cuff example and in reality, a non-issue as no production codebase would avoid pointers.

This unambiguous approach is something to make your peace with. Go has a way to do things and unless you want to take on pain for the sake of swimming upstream then you need to be prepared to accept it. To soften that message a little, I’d give the same advice for Javascript but that’s because it’s a fundamentally broken language but for Go it’s a reasoned position which makes a huge difference.

Libraries, Frameworks & Community

I often think Libraries and Frameworks show the health of a language. My small project wasn’t the most representative place to judge as I only really needed the core functions plus a couple of external libraries.

The core functions did what I needed - there's not really much to call out. they seemed sensibly implemented. I did think the documentation was nicely done; the live web-based examples are always a pleasure to see which helped me on a number of occasions as a quick scratch pad.

I turned to external libraries for IMAP support and JSON. For the latter I didn’t need to but the core implementation is a bit basic. Finding, installing and using both was pain free. The IMAP implementation was just fine, and the JSON library is superb! I’ll take that as a healthy indicator, somebody felt motivated enough to write probably the best JSON parsing library I’ve seen, and they did it in Go.

The Community side of things is harder to judge, my project was simple enough that I didn’t hit too many phone-a-friend scenarios, but from what little I did need there seemed to be a good degree of engagement on Stack Overflow et al.

Opinionated

I’ve seen the word ‘opinionated’ mentioned many times about Go. I think it’s an unhelpfully loaded term. I would describe it as ‘unambiguous’; it works this way, that’s how it works. I liked this clarity, even if I didn’t always agree with all of their choices.

I started my code without Pointers, purely to get a flavour of how switching between reference and value would be. It was irritating. Go has no unreferenced variables, so every declaration gives a you a blank block of memory for whatever the type is, yet pointers can be null. This leads to some obviously cumbersome scenarios. I don’t want to over play this point, it’s really just an off-the-cuff example and in reality, a non-issue as no production codebase would avoid pointers.

This unambiguous approach is something to make your peace with. Go has a way to do things and unless you want to take on pain for the sake of swimming upstream then you need to be prepared to accept it. To soften that message a little, I’d give the same advice for Javascript but that’s because it’s a fundamentally broken language but for Go it’s a reasoned position which makes a huge difference.

Libraries, Frameworks & Community

I often think Libraries and Frameworks show the health of a language. My small project wasn’t the most representative place to judge as I only really needed the core functions plus a couple of external libraries.

The core functions did what I needed - there's not really much to call out. they seemed sensibly implemented. I did think the documentation was nicely done; the live web-based examples are always a pleasure to see which helped me on a number of occasions as a quick scratch pad.

I turned to external libraries for IMAP support and JSON. For the latter I didn’t need to but the core implementation is a bit basic. Finding, installing and using both was pain free. The IMAP implementation was just fine, and the JSON library is superb! I’ll take that as a healthy indicator, somebody felt motivated enough to write probably the best JSON parsing library I’ve seen, and they did it in Go.

The Community side of things is harder to judge, my project was simple enough that I didn’t hit too many phone-a-friend scenarios, but from what little I did need there seemed to be a good degree of engagement on Stack Overflow et al.

Final Thoughts

I feel a deep sense of satisfaction to have finally done some Go, even if I’d hated it I’d still have felt a sense of achievement. My experience spanned maybe a couple of months of coding in front of the TV or on the move, so very much an on the side affair, but it felt involved enough to form an opinion with some validity.

I quickly came to liking it, which if it’s something I need to work with everyday that’s a pretty important thing to emphasise. The cleanliness of the language was a pleasure, albeit one that felt a bit spare at times, and the implementation of concurrency is a real standout feature. I waited ten years for it, and I’m left feeling glad I didn’t wait any longer.

What should you do?

If you’ve been thinking about doing something in Go then I’d say the barrier is fairly low, from a standing start you should be able to get productive fairly quickly with a minimum of frustration. It’s a nice language that felt rewarding and productive to code in, with minimal gripes around syntax or patterns. No language is perfect and its pitfalls for me at least were entirely forgivable. Go seems well suited to highly parallelised code although in my case there was no explicit need for that, it was just such low hanging fruit I added it anyway.

I would advise caution on starting out a brand-new production code base in Go. That’s not the place to experiment and nothing I touched on really gave me any insight in to when/how you could justify its use in a real project. At a minimum I feel that nearly a decade of a Jellyfish approach to Go and yet barely once seeing it ‘in the wild’ should throw up a little yellow flag that mind share in industry is not quite there yet. I’ll leave predictions of its future popularity to others.

Final Thoughts

I feel a deep sense of satisfaction to have finally done some Go, even if I’d hated it I’d still have felt a sense of achievement. My experience spanned maybe a couple of months of coding in front of the TV or on the move, so very much an on the side affair, but it felt involved enough to form an opinion with some validity.

I quickly came to liking it, which if it’s something I need to work with everyday that’s a pretty important thing to emphasise. The cleanliness of the language was a pleasure, albeit one that felt a bit spare at times, and the implementation of concurrency is a real standout feature. I waited ten years for it, and I’m left feeling glad I didn’t wait any longer.

What should you do?

If you’ve been thinking about doing something in Go then I’d say the barrier is fairly low, from a standing start you should be able to get productive fairly quickly with a minimum of frustration. It’s a nice language that felt rewarding and productive to code in, with minimal gripes around syntax or patterns. No language is perfect and its pitfalls for me at least were entirely forgivable. Go seems well suited to highly parallelised code although in my case there was no explicit need for that, it was just such low hanging fruit I added it anyway.

I would advise caution on starting out a brand-new production code base in Go. That’s not the place to experiment and nothing I touched on really gave me any insight in to when/how you could justify its use in a real project. At a minimum I feel that nearly a decade of a Jellyfish approach to Go and yet barely once seeing it ‘in the wild’ should throw up a little yellow flag that mind share in industry is not quite there yet. I’ll leave predictions of its future popularity to others.