The Gopher way

My journey from Python to Golang via Node.js

Ever since college, I dabbled with Python, made a few scripts here and there, built some websites with Flask and Django. It was the first language I referred to anyone because it was quite easy to start with and delivers amazing results to the developers in no time. Although it was quite a break from having to program in C and Java, the language felt lacking in some of the most important areas.

The deal breaker

Now came Node.js into my life and I began experimenting with concurrency patterns. Backend JavaScript? Who would take that seriously? At best, it seemed like a new attempt to make server-side development easier at the cost of performance, scalability, etc. Maybe it’s just my ingrained developer skepticism, but there’s always been that alarm that goes off in my brain when I read about something being fast and easy and production-level.

Node.js became my go to language for a very long time. I got used to the latest programming syntaxes with using ES7 in the client side of things. The single threaded node event-loop made miracles possible for me, without severing any kind of performance and being cheap to use. As time passed, I began a deeper dive into the world of npm modules and the architecture of Node.js. It is a magical space filled with unicorns and rainbows.

Performance at it’s best?

Most developers have heard about Node’s non-blocking I/O model; it’s great because it means all requests are handled asynchronously without blocking execution, or incurring any overhead (like with threads and processes) and as the developer you can be blissfully unaware what’s happening in the backend. However, it’s always important to keep in mind that Node is single-threaded which means none of your code runs in parallel. I/O may not block the server but your code certainly does. If you call sleep for 5 seconds, your server will be unresponsive during that time. But my actual problem came with JavaScript. I know most of you will feel this like a rant and there are ways to solve this problem. Please, just bear with me through this.

When people were developing JavaScript, they wanted it to do all sorts of things and serve every other purpose. It all started with those thoughts, which made it kind of messy. Determining boolean outcomes in plain old JavaScript is hard.

True? False? Confused all in all.
// all true
1 == '1';
1 == [1];
'1' == [1];
// all false
1 === '1';
1 === [1];
'1' === [1];

The internal type system of JavaScript is a mixed bag with six different types which causes all sorts of havoc to the end user.

  • Undefined (a variable with no defined value)
  • Null (a single null value)
  • Boolean (true or false)
  • Number (this includes Infinity and NaN – not a number!)
  • String (textual data)
  • Symbol (a unique and immutable primitive new to ES6/2015)

I know there are ways to sort out these issues effectively, but a language experiencing these sorts of mutability can be problematic to users who are new to JavaScript world.

Then comes callback hell and having to deal with Promises and all in all the new keywords async and await . I always had retrospection over my language choices and it became very clear that I need strong typing and I can’t live without it. I tried TypeScript but it seemed a bit too expressive to start with.

The Second Deal Breaker

My main requirements for a language were speed, reliability, built-in concurrency patterns, a strong type system, garbage collection and lots of tooling. It’s not until recent times that I felt the need for all this stuff. I heard rumours earlier about Google building a new language that is going to change the world. True to the naming, it really brought about some deal breaking changes. Here’s a list of things I like about Go and why you’ll love it too.

go get -u programmer.life/success

Opinionated

Yes, it is very opinionated. But, I love the way of doing things in one way. With Golang, to do something, there’s just one way of doing it. The official tooling built around Go ensures that we do follow the one way.

Goroutines

The first half of Go’s concurrency paradigms. Lightweight, concurrent function execution. You can spawn tons of these if needed and the Go runtime multiplexes them onto the configured number of CPUs/Threads as needed. They start with a super small stack that can grow (and shrink) via dynamic allocation (and freeing). They are as simple as go f(x), where f() is a function.

I am strong. Because my dad wanted me to be.

Channels

The other half of Go’s concurrency story. Channels are a typed conduit used to send and receive values. These are used to pass values to and from Goroutines for processing. They are by default unbuffered, meaning synchronous and blocking. They can also be buffered, allowing values to queue up inside of them for processing. Multiple go routines can read/write to them at the same time without having to take locks. Go also has primitives for reading from multiple channels simultaneously via the select statement.

Compiled Static Binaries

Go compiles your program into a static binary. Yep, you heard it right. A static binary. This makes deployment really simple. Just copy the binary and it should be good to go. No pip install, no virtualenv, no freaking npm install . All of that is handled at compile time.

Binaries defeat all!

Cross Compilation

This is too good to be true. But it is. Yes. Golang allows cross compilation. Ever wanted to test your program on Windows but you use Linux? No more installing compilation tools and building it again on windows. Simply, use GOOS=windows and you can compile it on a Mac or a Linux machine. It even allows changing the architecture of the platform with GOARCH . There’s a laundry list of available platforms and OSes. You can find the list here.

Runtime

Go is a complied language, but still has a runtime. It handles all of the details of mallocing memory for you, allocating/deallocating space for variables, etc. Memory used by variables lasts as long as the variables are referenced, which is usually the scope of a function. Go has a garbage collector.

Pass by Value

“For the love of all holy Mary, please don’t bring back pointers.” And, well, yeah, they almost averted the crisis in the case of Golang. Everything is passed by value, but there are pointers. It’s just that the value of a pointer is a memory location, so it acts as pass by reference. This means that, by default, there is no shared state between functions. But, you can pass a pointer if you desire/need it for performance/memory utilization reasons. Go does the right thing by default, but doesn’t shackle you. Oh, and there isn’t any pointer math, so you won’t screw yourself with it. As pointed out on HN, you can do pointer math with the “unsafe” package and unsafe.Pointer.

Type System

Go has structs and interfaces. Go’s structs can have methods associated with them. Structs can be anonymously included in other structs to make the inside struct’s variables/methods available as part of the enclosting struct. Interfaces are sets of method signatures. Structs implement an interface by implementing the methods described by the interface definition. Functions can receive values by interface, like so. The compiler checks all of this at compile time.

Package System

Or lack there of. Since Go compiles everything statically, you don’t have to worry about packages at runtime. But how do you include libraries into your code? Simply by importing them via url, like so: import "github.com/bmizerany/pq". Go’s tooling knows how to look that up and clone the repo. Also works with Mercurial, Bazaar & Subversion. go get it, my boy.

Anon. Functions & Closures

Go supports anonymous functions that can form closures. These functions can then be passed around and retain the environment under which they were created, like so. This can be super powerful when combined with channels and go routines.

Built in profiling

It supports pprof as a part of the standard library. And with a very small bit of work, you can access profiling info via a http interface.

Defer

I love to postpone work, yeah.

Ever forget to close a file descriptor or free some memory? So have the designers of Go. The reason for this is that you usually have to perform those actions far away from where the resources were opened/allocated. Go solves this problem with the defer statement. Each defer statement pushes a function/expression onto a stack that get’s executed in LIFO order after the enclosing function returns. This makes cleanup really easy, after opened a file, just defer file.Close().

Comprehensive Standard Library

Go’s standard library is pretty comprehensive.

It’s Fast

Go compiles down to native code and it does it fast. Usually in just a few seconds. No matter if you have a million package imports. It does compile in seconds. There’s a rumour that Golang actually came to life over a discussion developers had during a code compilation break.

Comprehensive Tooling Out Of The Box

Go do a go --help. Some of my favorites: go fmt, go doc, go fix go get go tool pprof& go test.

Straight Forward Testing

Super straight foward, no magic. And hell, yeah, it even has a race detector that finds out all your runtime woes even before hitting production.

Simple C Interface

By using build directives you can integrate with C libraries really easily.

Straight Forward Syntax / Standard Formatting

The syntax is pretty simple, C like w/o all the crazy of C. But what’s really nice is go fmt, which re-writes your code into the Go standard format. No more arguments like tabs vs spaces or indentation rules! There’s just one way and you have to stick to it my friend.

Golang’s Cons

With all of that said, it’s not perfect…

Runtime

Go’s runtime is not super tuned yet. By comparison the JVM has had over 18 years of development history behind it. But, for a 1.0.X runtime & language, Go is pretty damn good.

Garbage Collector

Go programs can malloc a lot of ram at times, even when the program itself isn’t using much. Most of this shows up as VIRT though, so most linux systems just won’t care.

1 CPU by default

This is going to change over time, but the runtime will, by default, use only one CPU.

I can bear with having a few cons. After all nothing is perfect in this world. I can live by knowing that there are a few disadvantages while using a language as long as they don’t affect programming like JavaScript’s falsity or callback hell.