5 Reasons Go Will Take Over the Programming World

The Go programming language has been around for a bit over a decade now and has been gradually growing in popularity. Here are some reasons why, and why you might find yourself wanting to write your next project in Go.

1. Just the Right Amount of Runtime

The “runtime” of a programming language is the code that comes with it and is necessary to support the features of the language itself.

Go’s runtime is significantly larger than e.g. C++, but with good reason.

When I look at the runtime features included in the language I see a pattern:

  • Garbage collection - you don’t have to manually allocate and deallocate, Go takes care of this for you
  • Goroutines - efficient multithreading and I/O
  • Channels - for pushing data between goroutines
  • Interfaces - minimalistic but efficient dynamic type assertion
  • Slices - used in the same way as one would use an array in another language, but designed to deal with allocation more efficiently (and lazily in a lot of cases)
  • Maps - good old key-value hash maps built into the language with a clean syntax
  • Pointers - just like C except no pointer arithmetic, values vs pointers to them are clearly indicated, but without the stuff that causes segmentation faults and other crashes
  • Type safety with type inference - everything has a type but there are shorthands to not have to write it out in every case (e.g. “a := b” makes “a” with “b”’s type)
  • The language is also noticeably devoid of certain things like exceptions and the elvis operator “?:”.

What’s the pattern? These are things that almost every application needs, and so were deemed important enough to put in the runtime. But, they also showed some restraint on what they added and focused on the features that give the “biggest bang for their buck”.

Most languages have a sort of strange combination of arcane features that have crept in over time but are only useful for very specific cases and thus should be in a third party library (consider Javascript operators like “>>>=”, or the myrad deprecated methods associated with various built-in types).

With Go, I think their runtime and corresponding language features, as well as their standard library, much more closely match what application developers actually need.

2. Go Code is More Verbose

Yes, you read that right, it’s a good thing that Go code is more verbose. To understand why, you have to consider the reasons and means of making code less verbose and look at what it costs you.

On the surface, shorter would seem to be better, right? Why write “function” when you can write “=>”? Why write out “Object” when you can just use “{}”?

The answer is simple: Cognitive overhead. Or, to put it another way, every time you add a new feature, it’s another thing people have to remember and think about.

The effect gets worse over time as new features are added and code in one language that used to be current now becomes dated because of all of the new fangled features that have been added. C++ code written today looks quite different than it did a decade ago. In that case it’s not so much about the language itself but around the best practices and common libraries used, but the point is the same: While it’s good that things are improving and people are finding better ways of doing things, inventing new ways to do the same thing gets tiresome and adds unnecessary complexity.

Sometimes I feel like someone needs to just tell the Javascript people that it shouldn’t be someone’s job to keep adding things to the language. Heck, maybe they should hire someone to start deleting things. Just a thought.

Other conveniences that I think are a bad trade-off in other languages are things like exceptions. In Java (and JS) you can surround a huge block of code with a “try” and then give a block of code to handle any error that happens in there. And so how do you think most novice developers utilize “try” and “catch”? You guessed it, to take some block of code that is misbehaving and mute the error. So much for trying to write good, reliable software.

Go addresses this by allowing functions to return multiple values, and by convention one of these is often an error. Then you just check the error. You know, with an “if” statement. It’s ugly, it’s verbose, it’s terrible, children run and hide in the presence of the big bad verbosity of it. And yet it works really well. And when you’re done complaining about having to type all of those “if err != nil {” lines, Google “[your editor name] snippets” and find out how to make yourself a shortcut for this.

3. Now that it has Generics, there is nothing left to complain about

For a long time I’ve repeatedly heard how people didn’t want to write Go code because it didn’t have generics. And I’m really glad they added this feature. Not because I want to use it, because I dislike listening to people complain about it. Generics are one of these things that is apparently fairly simple, but has a lot of nuance under the hood and hidden complexity. Consider the fact that C++ templates and Java generics look very similar in terms of syntax, but they are completely different implementations under the hood. This sort of complexity you can ignore as a developer, but has a huge maintenance impact on the language itself. Regardless, now that it’s done, people can’t complain about it and can go about discovering the fact that only a small percentage of the code once written typically needs or benefits from generics.

Don’t get me wrong, having types as parameters can be very useful for specific situations. I just think these situations are much less common than the “ugh, Go doesn’t have generics” people typically think.

4. Awesome Build Tooling

The fact that just files in folders and one simple go.mod file can produce reliable builds with exactly the same source code input on different systems sounds simple and obvious, but it is literally exactly what other build systems do not do. Try building a node.js project, waiting 6 months and running “npm ci” again and see what you get. Build problems are a constant source of pain, and Go finally got this right.

The key concepts here are that one set of compatible APIs gets one import path, and this idea of (https://research.swtch.com/vgo-mvs) [Minimal Version Selection].

To break this down a little further: The first concept is a major one, and one that most other version systems get wrong. And the idea is that each unique incompatible version of a module should have a unique import path. In other words, if you have version 1 of library X and version 2 of library X, guess what? These are not the same thing, and thus need different import paths. With Go modules they introduced the idea of appending “/v2” to the import path to correspond to module version 2, etc. You can read up on the details and there are a few bits and pieces to learn, but I think they got the core idea right here: If you make breaking changes to a library, it gets a different import path. This simple practice has important side effects that greatly improve reliability and reduce build confusion.

Once you have that concept in place, then Minimal Version Selection takes it a step further by saying that we should not just “use the latest 2.x” library, for example, but instead we should upgrade dependencies manually and only use a tested version of a library that has been indicated in build configuration (go.mod). This makes it so that if you build a piece of software today and run the same build tomorrow, you are guaranteed to get the same output (having used the same exact version as input). Now of course we want to keep up with upgrades and security patches and the like, but the idea is this should be done explicitly, and not randomly as part of the build process.

These changes made building Go programs a dream as compared to many other build systems.

5. Type Safety for Large Projects is a Must

Plenty of other languages have type safety as well, but JavaScript and Python are excellent examples of languages which are productive for smaller projects but can easily spiral out of control due to their lack of type safety.

Modern editors ease this pain a bit by giving reasonably good error notations for common cases even in type-unsafe languages like JS and Python. But this doesn’t change the fact that as your application grows in complexity, if you are using a language where there is no checking on any of the field or method accesses done except at run time, sooner or later you’re going to write some code that seems fine for now and then suddenly explodes in production and at the worst possible time due to some silly typo. A typo that would be prevented with a compiler and a type safe language. You also get this same benefit while editing code. Having “a.SomeThing()” error at the compile step (since it should have been let’s say “a.Something()”) saves you time because you didn’t have to run that line of code to find out, you just had to compile.

Go’s type system, while controversial for its differences from other languages, has all of the major benefits you’d expect from any other type system. Use them for Good.

In summary, while it’s only a decade old, Go has already made major strides in becoming the de facto standard for solving business programming problems with many teams. Will it actually “take over”? Who can say. But I think there are a lot of convincing arguments that make it steadily continue to gain ground. And Rome wasn’t built in a day.

Share Comments
comments powered by Disqus