This document contains a summary of the speaker notes used to present this talk to accompany the pdf snapshot of the slides.
These notes can be a little abstract and more aide-memoirs to the speech giver, and are not intended to be read as prose, so forgive the fragmented sentences!
- 7 Deadly Gopher Sins
Hi and welcome to my talk.
Today I’m going to be taking you through what I feel are 7 of deadliest sins in Go.
These are mostly very much things I myself have been guilty of or experienced at least once in my career, so hopefully this may save you some of that pain!
Some of these are going to come with examples, some are going to be more my relating experiences, but they all come with gophers!
To begin at the beginning, who am I?
I’m John
Some of you may have seen my talk at Gophercon UK last year and I’m honoured to have been asked back again!
Currently I’m a senior engineer using Go at Admiral Money.
I’m also an artist, dabble in circus and fire skills, and ich spreche auch ein bisschen Deutsch!
So without further preamble, let’s get started and because we all love Go
…let’s start with Lust.
But I’m not talking about love here, so… what?
Let me take you back for a moment to when we were kids. We’ve all felt it - toy with action features rush to play - arm comes off
Go is that toy - want some of that
Ok in hobby project, but Go is sold as really easy to use, so they rush to build them into production solutions.
But why problem?
Tempt to Go - features - goroutines
Start with how they’re sold
Well, I’m sure you’ve probably all seen the “sales pitch” which is very like that toy advert on TV.
“...BOOM! Goroutines!”
Except… that’s not really true.
I mean it’s technically correct (you know, the best kind of correct), but it’s not the whole story.
Anyone that’s done anything with goroutines and probably tell me straight away what’s wrong with this code (audience question)...?
More than just the keyword.
Start with a straightforward function to convert then sum some numbers.
Then think - but we should be using goroutines!
Once we introduce goroutines however, the code is twice as long and we suddenly have a whole new set of considerations:
- Can’t just stick go in front of our string conversion.
- Can’t just increment our sum directly anymore as multiple goroutines can’t safely write to the same variable.
- Can’t just return an error as we did before as when you add the go keyword it doesn’t allow return values. So we need another channel (or errgroups or similar)
- And finally we need to wait for the goroutines to do their work and receive the results.
no notes
So from lust to wrath
Who remembers Hitchhiker’s - Don’t panic!
By which I’m referring to runtime crashes, not running around screaming!
But by Don’t Panic I’m not saying it’s a sin to not write perfect code, because out of control…
Firstly relying on panics as an error handling strategy, treating them as exceptions.
Analogy (see slide)
Both scenarios achieve the desired outcome of stopping the car.
But real sin is intentional panic
But as I said, the sin here is about intentional panics. And now we must talk quickly about must as this is a very common sin.
You see, in Go, by convention a function that is prefixed with Must will panic if it errors - nothing enforces this btw, it’s just convention.
If there is a must, then there will generally always be a vanilla version of the function without the prefix that returns a normal error.
A common example of this, and one I’ve seen sinfully abused a lot, is in the regexp package.
In the first example we’ve dropped a Must into runtime code - this is bad.
For one, it’s inefficient as if the regex doesn’t change, you’re compiling it every request. And more importantly, if you’ve done something wrong and it panics you’ve broken your users’ journey If you really need to compile at runtime, check the error properly so you can return something sensible to your user.
But in most cases you probably only need to compile your regex once - so pull it to the package level and use the must to ensure that if it fails to compile it’ll fail fast at startup rather than when serving your users!
no notes
How can we be greedy? They say you can’t be too prepared. But is that really true?
None of us can see the future. Some reason in coding we have a habit of attempting to do just that.
Knowing that you’re working on a feature this sprint, and knowing that in the backlog for next sprint another feature is coming up is one thing.
The sin is to pre-emptively over-engineer and genericise to greedily add functionality to solve problems that not only do you not have yet - you may actually never have.
But why’s that a problem? What’s wrong with future proofing and writing flexible code?
(explain hello world)
Maybe we should … (from the slide)
…and very quickly we suddenly have a behemoth of a service!
Why is this bad?
Now 30 second change is a 6 month rebuild.
no notes
So after all that over engineering, it’s time to slow down with some sloth
You’ve probably heard how laziness can be a virtue, but how so?
And how does working harder allow us to be virtuously lazy?
Well it’s about putting in more thought upfront to reduce cognitive overhead later - whilst of course still avoiding our previous sin!
Well, let’s start with comments.
What we don’t want is this - What I wanted was this.
The other area where context is often skimped on is errors, and this I think is an unfortunate side effect of the if err != nil { return err } idiom.
The intent of forcing you to check errors is ostensibly to encourage you to take action as close to where it occurred as possible. As touched on in wrath this avoids the need to wade through stack traces etc, and is more efficient in ensuring we don’t waste time and resources on processing anything else if we already know we’ve failed.
But many times, what you will actually see is a whole chain of if err != nil {return err}, which in effect simply bubbles the original error up several layers, by which time the context is lost.
Better to where possibly if you can’t definitely handle an error at site, add context.
no notes
So what is our sin of gluttony?
Well, you know when you go on holiday, and when you get there you realise you’ve packed way too much?
Not only
- are your cases heavy and hard to lift;
- it’s hard to find anything in your cases without unpacking the whole thing;
- but you probably don’t need half of it anyway.
Standard “holiday stuff”
Well the same is true of libraries and frameworks in Go.
Just like those things you pack - they can be really useful for the right situation - but if you don’t need them, they’re just extra baggage.
So the sin here isn’t using them when you need them, it’s using them just because.
And why do we do this?
Again, just like the packing scenario, a lot of it is habit. Especially if we have a background in other languages where third party frameworks etc are more prevalent.
But why is it a problem?
Every dependency:
- Can go away
- Can have a vulnerability
- Can possibly lock you into an opinionated path you have to spend effort working around.
no notes
And so to envy
Well this is probably one of the commonest gopher sins I’ve seen, usually in engineers coming from other languages - and I know when I first used Go it was the one I was most guilty of myself!
Essentially it’s the tendency to write Go as if it were another language.
Why is this a sin though?
I mean, as long as it compiles and runs without error, what’s the issue?
Though ultimately it’s usually a recipe for making your code more complex and less maintainable than it needs to be. To return to our sin of sloth and greed, this a good example of doing something you think is for the best, but in the end just makes your life harder.
But what are some examples of this?
Let’s open with some straight forward logic flow.
I’m sure we’re all used to nested code like we see on the left, and it’s very much how I coded back when I was using Python.
But as we can see from the code on the right, we can, we a few simple tweaks, make it much cleaner and easier to understand.
It’s down to 14 lines instead of the original 15 - and that includes some added whitespace for readability!
By inverting some of the logic - ostensibly checking the negative path first - we eliminate the need for the nesting and keep the flow clearly in line of sight.
no notes
And then we get to pride.
But I’m not saying taking pride in your code is a sin!
The gopher sin here is where you assume you know what’s best for others.
How so?
Well, have you ever been looking for a library to do something, because of course applying the virtuous interpretation of laziness, you don’t want to have to write it yourself if someone else has already done it, but despite finding some, none of them quite work the way you need?
That’s not saying those libraries are bad - they may be incredibly good at what they do - but they’re overly opinionated in a way that was perfect for the author but doesn’t work for you.
Example of entropy checking function
no notes
no notes
Sin of complexity pulling it all together.
no notes
[Slide not presented - included for referencing and attributions]