Three Reasons We’ll Be Writing More Go in 2016
In solving the future, the engineering team at WINTR is always faced with a fine balance: tracking down and experimenting with bleeding-edge technologies, and basing our client work on proven technological solutions. Individually and as a department, we’ve kept our eye on and played with some technologies that looked promising (and were fun to play with), but ultimately couldn’t add value to our client work. But the more time we’ve researched Go, Google’s relatively new programming language, we feel we’ve found a language that plays perfectly to that sweet spot between the avant-garde and the reliable. I’d like to take this opportunity to describe three of the reasons we’re excited about Go.
Go is a systems programming language, which means that it enables direct access to low-level components like the computer’s physical memory and other hardware. It is also one of a growing number of languages that endeavor to take advantage of modern processor architectures by putting several processing cores to work simultaneously.
With that basis, let’s take a high-level look at some of Go’s greatest strengths: runtime-independent binaries, automatic code quality checks, and built-in support for both synchronous and asynchronous programming.
Further, a compiled Go program contains all the dependencies it needs to run. Whereas to run a Java program, for example, you need to have the Java virtual machine running on your computer, you can run a compiled Go program on any platform, even without having the language installed. This makes Go programs truly portable, decisively fulfilling Java’s dream of writing once and running anywhere.
Automatic Code Quality Checks
As we’ve worked through The Go Programming Language, we’ve written a fair amount of Go code. As a result, we’ve developed a close relationship with the compiler. Many of us have a text editor plugin installed that performs two compilation-related tasks each time you save a file: it runs simple quality checks, and it automatically formats the code. Let’s look at these processes individually.
The more Go code we write, the more we realize that if it satisfies the compiler, it’s likely to work as we expect. The compiler will warn you if you try to save code that contains a variable that you declared but didn’t use, try to pass a function an argument it isn’t expecting, or one of many other minor infractions. While these infractions are easy to make, they can be time-consuming to track down. With Go, you simply save a file, and the compiler tells you where in the file the transgression occurred, often accompanying the warning with a hint as to how to solve it. This way, you can find many common causes of errors before compiling your program. It can often be annoying to be reminded of something like not using a variable, but the language guidelines are designed such that if you’re able to satisfy the compiler’s strict specifications, there’s a great chance that the code does what you intend.
Disputes about whether to indent code with tabs or spaces or whether or not to terminate lines with optional semicolons have been costly in some organizations. Part of being a developer, it seems, is having strongly held beliefs about even something as seemingly insignificant as which type of invisible whitespace character to use.
In Go, this is no longer the case, thanks to gofmt, Go’s automatic code formatting. Write your Go code how you want, and gofmt will automatically format your code according to the universal Go standard. Regardless of the opinion an individual developer holds on a certain aspect of code style, his or her code will ultimately conform to the stylistic rules of the language, as dictated by the core team: in Go, style has become objective.
While this might sound constricting, we’ve found it to be quite freeing. This feature not only saves a great deal of time and money by circumventing unproductive discussions about whitespace, but it also ensures that reading through third party code always looks familiar.
Synchronous & Asynchronous Code Execution
As the need for more performant systems increases, the ability to use concurrency – that is, to structure the program in such a way that it can take advantage of processor multitasking – becomes ever more appealing. And yet, while it promises substantial performance increases, concurrency comes with some of the most notoriously difficult programming problems regarding the communication of shared data between processes. Once you leave the comfort of synchronous, sequential code, and wander into the asynchronous world of concurrent processing, the type and difficulty of bugs that arise can be dizzying.
In short, there is a time and place for both synchronous and asynchronous programming.
In Node.js, all code is run asynchronously by default. It is unique in this regard, and as such, it can create confusing bugs when code that is declared later in the program returns values prior to code that is declared earlier in the program. (There’s a classic programming joke around this topic: “What do we want?” “Now!” “When do we want it?” “Concurrency!”) Developers new to concurrency are particularly vulnerable to this confusion. There are often ways to synchronously handle code, but they can be difficult to maintain and veer away from the generally accepted style. As a result, support for asynchronicity might be lacking in third party libraries, or it might be a headache to get a particular piece of the program to run without continuing on to the next line while, for example, it’s waiting for a response from an external API.
Ruby is by nature synchronous, and while it supports asynchronous programming, it can be difficult to write a program to take advantage of concurrency. Anecdotally, it is said that Ruby on Rails core team member José Valim was fed up enough with Ruby’s lackluster support for asynchronous programming that he created his own language.
Go, while synchronous by default, makes asynchronous programming simple through lightweight threads of execution called goroutines. Goroutines are a built-in language feature, and the code makes it clear when they’re invoked. Because of this clarity, it eases the cognitive burden of predicting what code will run and return when. Thus, unlike Node.js, it makes concurrency explicit, and unlike Ruby, it makes concurrency convenient.
As a team, we’re still relatively new to the Go programming language, but we’ve built enough personal projects with it to be excited about its potential. Our department will be continuing our book club in the New Year, digging further into Go’s features and exploring the possibility of its use. Do you have a project that requires extremely performant, efficient, and scalable web application development? Drop us a line at [email protected]
Filed under: Practice