Some notes when you get into Go from Python.

Errors - Go has the complete opposite error philosophy as Python. Python says to ask forgiveness not permission. Go checks errors religiously after calls and does something about them. Go avoids exceptions. Instead it has error return codes.

Hidden errors - Though sometimes “errors” have to be explicitly checked for the resulting object in like an HTTP response. Like resp.IsError or checking the status code. This can hide errors if you don’t look for them.

Go output not that readable - Go test etc output is pretty ugly compared to Python / pytest etc and hard to understand.

Slices - Go uses arrays and slices for everything. Slice is literally a view into an array at a begin, end and capacity. When you append you might end up allocating more capacity.

Structs for types - Go uses structs for types. Python defaults to classes

Methods - Go methods are things you can do with structs. They’re declared independent of the struct itself. While Python looks a bit more like classic OO. This means you could extend an ‘object’ with more methods elsewhere with more functions that handle structs

Public methods all caps - Public / exported methods and members in Go begin with a capital letter, and the language enforces this. A bit strange if you’re used to a capital letter meaning a type, but you get used to it.

Typing - Go is statically typed, Python dynamic type by default.

Interfaces are different - Go has interfaces - a set of methods an implementor must fulfill. But unlike other languages you don’t “inherit” from the interface. If you try to pass your PekingDuck to a method expecting a Duck it’ll just compile-time error if you try to call Quack

Interfaces are structs - Interfaces can be somewhat confusing, because they seem to really be structs that hold information about the concrete type and a pointer to the value. So an interface itself not being nil doesn’t rule out the Interface’s value being nil. This is a confusing footgun that seems to trip everyone up.

Go has built in bells and whistles - Go has a lot more default bells and whistles (expected project structure, packaging, test tools, etc). Much more he developer expects is owned by the language. Fewer arguments about whether to use pip vs poetry vs uv vs conda etc.

Except maybe debugging bells and whistles - In python to debug, you can just put a breakpoint in the code and run with your normal interpreter. In Go there’s delve, a different tool, that runs code differently than Go. Which is annoying to translate between go test and dlv to run your test.

Context built-in - Go has a default context in its standard lib for processing requests. It’s just assumed to be there. Python libraries for implementing services usually have a similar concept, but Go makes it more first class.

Concurrency - Go focused on concurrency with goroutines and channels. Not to mention it’s native and has no GIL like Python.

For all these reasons Go really excels at building backend services. While Python has its large amount of numerical processing with numpy, pandas, polars, tensorflow, etc. Though one wonders if there’s anything particularly special about Python that makes this possible, and maybe we shouldn’t be assuming Python will always be the tool for those things too?


Doug Turnbull

More from Doug
Twitter | LinkedIn | Mastodon
Doug's articles at OpenSource Connections | Shopify Eng Blog