Goodbye Python. Hello Go.

By Jake Wilson

543px-GoLanguage_Logo_2018-04-26_BlackWhite.svg

I’ve been using Go for a lot of the tasks where I used to use Python.

Some examples:

  • Processing Cloudfront logs stored in S3
  • Moving Terabytes of files between buckets and/or regions in S3
  • Matching up files between our database records and S3 ensuring everything is in sync.

Most are one-off tasks which is why a scripting language has been ideal. The program needs to be written quickly and then most likely thrown away. Usually the task is something new and unique, so code reuse is minimal.

Below are some advantages of using Go instead of Python.

Having a Compiler is Nice

I make stupid mistakes in Python constantly. I misname a variable or a function or I pass in the wrong arguments. Devtools can catch some of these, but they usually require special setup. I’ve never been able to configure pylint easily, and I’m not a fan of full blown IDEs that require their own configuration.

The worst is if you mistype a variable that’s hidden behind conditional logic. Your script might run for hours before triggering the error, and then everything blows up, and you have to restart it.

Unit tests would catch most of these, but it’s hard to get 100% code coverage, and I don’t want to spend time writing unit tests for a one-off script.

Compiled languages make all these problems go away. The compiler catches all the silly things you miss. Because of this, I prefer languages like Go for anything over a couple hundred lines.

Development Speed

The flip side to having a compiler is that usually your development speed decreases. This is especially true with C/C++ and Java.

Go is simple enough where I found the development speed hit to be minimal. Don’t get me wrong, I can still write code faster in Python, but I probably achieve 85% of my Python productivity in Go.

85% isn’t bad when I consider how many fewer mistakes I’ll make with the benefit of the compiler.

Better Parallelism

As you probably know, Go was built from the ground-up for parallel execution.

On my team, we usually need parallel programs because we’re dealing with a lot of data in S3 or in our database.

If the task is IO bound (which many are), then we can successfully employ Python threads. But if it’s is CPU intensive, Python will suffer because of the Global Interpreter Lock.

I also enjoy how simple things “just work” in multi-threaded Go without doing anything special. Ever had that problem where you Ctrl-C your multithreaded python and it doesn’t do anything?

Easier Deployment

I like having a single binary. I usually run code on EC2 machines to give my scripts closer network proximity to S3 and to our database. With Python, I have to ensure all the packages I need are installed on the remote machine, and that one of my coworkers hasn’t installed anything conflicting.

Virtualenvs solve most of this problem, but I still find Go easier.

Usually I cross compile my code on my Mac to Linux, copy it to the remote machine, and I’m off and running. All my dependencies are contained in my binary.

Consistent Styling

At first, the gofmt tool annoyed me, particularly their choice of using tabs instead of spaces. I thought that was insane.

But as I use it more, I’ve come to depend on it. I get free formatting right out of the box. All of my code is always styled consistently, regardless of what project I’m working on because the formatting is a feature of the standard Go tooling.

I have to put in more effort to get the same effect in Python. I have to configure pylint correctly and then ensure it’s used in every single project.

Better Tooling

Gofmt is just one example of a general theme. All of the editors I love – VSCode, vim, and Sublime Text, all have great Golang extensions that take advantage of the standard Go tooling.

As a result I get intellisense similar to Java, but without using a real IDE. I’ve never come close to that ability with Python.

Disadvantages

Whenever I read posts criticizing Go, it’s usually because of the obvious features that are missing, like generics. I’ve never had much trouble with missing generics – you’d be surprised how much you can do with maps and slices, but I have had numerous other problems.

Go is opinionated

First, Go is probably the most opinionated language I’ve ever used. From forcing you to use tabs instead of spaces (assuming you’re using gofmt), to forcing you to use a certain directory structure, to making you code within the GOPATH environment variable, there are many features of Go which are not easy to change.

One of the reasons it’s so easy to learn is because you can’t change these features. If you don’t want to export every name that starts with a capital letter, then too bad for you. Fortunately, none of these are deal breakers for me, but I could understand if they are for others.

Python is much more flexible.

Somewhat Poor library support

It’s not fair to compare Python and Go in this arena. Go is a lot newer, but I’m still baffled when I find features that Go doesn’t support out of the box. I’m even more baffled when people on StackOverflow post code which should be a built-in function, and then act like there’s no problem with every person copying and pasting that code into their project.

2 examples that come to mind in the last couple of years:

  • Sorting a slice (fortunately this was made easier in Go 1.8)
  • Math.round only working with integers and not allowing you to Round to float values (e.g. if you want to round to the nearest .5). And before Go 1.10 there wasn’t even a math.round.

Granted, some of these are because Go doesn’t have generics, and some are because the developers of Go are following the strategy of only adding things to the standard libraries which are absolutely necessary.

I understand both points, but it’s still annoying when you encounter trivial functionality that you have to code yourself.

Hopefully as the language continues to evolve, these pain points become fewer and fewer.

 Do you have experience comparing Go with Python? Leave me a comment below.