Learning Go as a Javascript Developer

Image provided by Ben White via Unsplash

Over the last 8 months of my career at CrowdRiff I’ve made the transition from being a front-end developer to a back-end developer. When starting at CrowdRiff I was working on our client-facing interface which utilizes React/Redux, and then transitioned to being the go-to person working on the Node-side of our product. I really liked the shift to working on server-side code. When working in React I liked manipulating data, managing data, and making data show things on a screen. When I made the transition to working exclusively on then Node-side of our product, I felt like it was a great shift for me. Most importantly I was constantly challenged and learned a lot every day.

Shortly after I began working on the Node server, the team at CrowdRiff decided to make some architectural changes to the product, and the Node side of our application began to get lighter and lighter as we offloaded more of its responsibility to our API (which is written in Go). After a week or two of very little work on the node-server, I expressed my desire to join the Go team. I switched teams almost immediately and was flung head-first into a ton of challenges.

This article will illustrate some initial observations about what I found to be the key differences between JavaScript and Go.

Static Typing

Static typing was the first adjustment I had to make when learning Go. I had worked with typing previously in Node, but only in the capacity of GraphQL. While GraphQL implements typing, it’s fairly introductory and it is used for providing the user with meaningful error responses if they send along unexpected data. While GraphQL was a great intro into the idea and key benefits of strict typing, there was a great deal to learn and get used to when working in Go.

What is my biggest take away? Strict typing is now my preferred way to program. When creating a program that is meant to last, strict typing pays off immensely. Often times in React (especially when working with state management libraries) when I return to code written months previously, I feel left in the dark with data I am working with. I often find myself wondering about the fields and the data types contained when working inside a large object. You won’t know until you run the program and investigate — or worse, when an unexpected type makes its way into your object and throws an error.

In comparison when, returning to code I’ve written in Go all the types are laid out for me. I know exactly what all data looks like at all times — leaving only the logic to reorient myself with. While this might not seem like an obvious boon to most people, I have found it invaluable when solving bugs or improving/adding to previous codebases that I have little to no experience with.

Interfaces

Interfaces are an extension of the previous topic of static typing. However, interfaces are such a different concept from anything that exists in JavaScript, it’s worth talking about the idea of interfaces in isolation.

In my short experience with the language, interfaces play a huge role in how a Go application is shaped. An interface dictates what methods must be present on a class for it to be considered that interface. The most accessible example I can illustrate is the error interface which exists in go. There are many functions in the Go standard library which make use of the error interface. The error interface states that in order for something to be considered an error, it must have a method of Error() which returns a string — it can have more, but it must at least have that method. If it does, it can be used in any place that allows you to use an error.

As a JavaScript developer, this simple example didn’t seem that impactful. As I worked more with Go, I began to understand the power of interfaces and how they can make code extremely flexible and interchangeable.

Expanding on the explanation of the error interface, we could replace every error that gets returned with our own custom error type. This custom error will have the Error() method, which we can add new functionality to. Inside this custom Error() method, we can add custom logic — like sending the error to a custom logger. We can also now mock this method easily, and with our custom Error() make testing more flexible by adding testing inside of the method itself in our mocks.

Mutability

While JavaScript is known to be an object-oriented language, a lot of modern implementations of the language involve following functional programming styles. This is especially common when working with React and Redux (a popular state management library). Redux requires the developer to understand immutability, and replacing pieces of data as opposed to updating data directly. This results in functions returning copies of data as opposed to functions which change the original data.

Working in Go involves a lot of mutation. As a JavaScript developer, I found this hard to understand. Isn’t immutability the way to program? If we change the underlying data, aren’t we susceptible to bugs and unknown behaviour? A lot of these concerns are weeded out by strict typing. Go makes heavy use of mutation, passing around data and mutating it directly between many functions. This was a lot to get used to, and something I continue to forget is a I can make use of.

Multiple Return Values

In JavaScript, a function can return one thing. In Go however, a function can return as many things as you’d like. For me this was quick to understand and accept, but seemed like a trivial benefit. It wasn’t until I had been writing endpoints for our primary API then I realized multiple return values helped with writing servers immensely. Why was this? With multiple return values, we’re able to write more robust error handling and respond with better error messages to our requestors.

When writing a server, there are multiple steps in which an error could occur. In JavaScript, we don’t always have the option of returning an error instead of an expected type. If a function is expected to return an integer, but something in that function makes it error (like an HTTP request to another server, for example), then what is it we’re supposed to return? We could return an object which has an error field nested in it. But doesn’t that mean we should always return this object? If there is no error message, is the error field that, but blank?

In Go we have separate returns, and it’s idiomatic to have most functions return an error (an error being something that looks like the error interface mentioned previously). Once we run the function, we can check to see if an error was returned (in addition to anything else) and handle it accordingly. During this error check, we can see what kind of error was creating. Was it a problem decrypting some JSON? Then a 400 error response should be issued to the requestor. Was it a problem with an internal request? A 500 error should be issued in this case. We can respond with much more granular errors and avoid inconsistent and hard to maintain systems like JavaScript objects.

JavaScript or Go?

I am really enjoying my time with Go. It was a huge challenge to make the switch from writing JavaScript, but now that I have more experience, it’s hard to go back. Once I began working in a statically-typed system, I really started to recognize its value in catching bugs early and not allowing a programmer to make trivial mistakes that add up to a lot of wasted time. Moreover, I find returning to previously-written code extremely easy. Less context is required to understand the logic in Go because I am able to see what the shape of all the data is expected/required to be. From my limited experience, Go seems to be most well-suited to the types of projects which are meant to last.

I still use JavaScript for building things quickly. The benefit of writing a Node server is how quickly you can get something on its feet with little time investment. It is hard to say if I feel this is true because the language is quicker to write, or because of my familiarity with JavaScript. Now when writing a project in JavaScript, I miss a lot of the features of Go. As a result, I’ve started exploring tools like TypeScript or Flow which add static typing to JavaScript.

I recommend taking a leap and writing things in Go. The learning curve for is very steep, but the payoff is extremely valuable. I feel much more confident in my ability to work with and manipulate data, and being familiar with another programming language has really increased the tools I have available for implementing solutions.