Go lang

It has been a crazy few months in startup land. The interesting thing for me about startups is no matter how crazy it is compared to corporate work, I find myself really content amidst the chaos. The big change here is we have decided to build our backend architecture in Go instead of Java. Having done Java for 19 years this is a big change, but for business decisions we decided that the trade offs with Go were better for our long term business needs than the trade offs with Java. Now that I have been using it for a few months I figured I would discuss some of the differences between the languages and what I like and dislike about each.

First Impressions

Just from an initial impression to me, Go seems like a cleaned up C language. You basically have a low level language with primitives and structs, slices (lists in Java), maps, and not a lot more. You end up having to build and write more code than in Java. But in sort of a nod to the good parts of OO they have interfaces and methods that you can attach to structs. At that point it is almost like a super lightweight C++.

I find that I spend a lot more time wiring up code in Go. Having done Spring now for 10 years you spend very little timing wiring up your code as everything is autowired, and you mostly just focus on business logic. Same for SQL Spring Data provides such a nice DSL you don’t write that many database queries either. In my initial analysis it took about 10 times as much code to write a simple REST backend in Go as compared to Spring Boot. But it used 1/10 as much RAM or less. This is the reason we decided to spend more money on Development costs (as it is much more code to write than Java) as we believe the savings on cloud hosting costs down the line will make it a better decision for us.

Go produces a single binary just like Spring Boot gives you a fat jar so I consider them equal here, but the startup time for Go seems to be instant, where as due to all the reflection and wiring at startup in Spring it seems like those apps typically take about 5 seconds to start in my experience (if there is a database involved).

Ceremony

General

Go has taken some steps to remove the ceremony in the language. One of the big differences that I notice is that you no longer needs ()around your statements in for loops, while loops and if statements. I repeatedly find myself putting those in by default and then having the IDE remove them. Another big difference is you no longer have to terminate your statements with ;as the compiler automatically puts those in for you. A little bit like Groovy in that regard.

Iteration

You would think that after doing all that work to remove ceremony that the language would be clean in general. But here is where I see the low level C type stuff come through. In Java if I want to iterate through a list it looks like this:

for (item : list) {

Basically it reads as for each item in the list, and the you operate on it. Go has a range statement to iterate through an array or slice. Here in lies a big difference in Go between Java is that you can return multiple values from a function or method. The designers of the language decided to have the range statement return both the index and the value. Generally in Java if I am using a for each it is because I don’t care about the index, I use a standard for loop if I need the index. Go has the standard for loop but for some reason they decided to return the index as well. You end up having to throw it away if you don’t care about it which feels like more ceremony again so the above statement ends up reading:

for _, item := range list { It is hard to look at that and think that reads better than the Java even though they got rid of the parenthesis.

But even this sort of misses the point as I am not even using for each that much in Java these days instead we have moved to a higher level yet and now use stream operations and handle everything in a functional style. What ends up happening in Java is you spend your time telling the language what you want to happen and let it sort the details of how to do it (lazy execution, or parallel execution, it doesn’t really matter with streams). In Go you spend your time telling the language how you want it to do the iteration so it feels like you are back down in the weeds again.

Initialization

Initializing an empty list also feels like it is more ceremony in Go. In Java you might have:

var list = new ArrayList<String>();

You look at that line and you have the ceremonly of the generics and the () to state which constructor you are calling and then the semicolon to terminate the statement. In go you end up with something like

list := []string{} again I wouldn’t call that better, just different. The ceremony here is the := which allows you to leave out the var at the front and you have the braces at the end which are initializing the struct. It is shorter but again neither seems to read better nor worse, just different.

References

One other big difference which really brings me back to C is you have to specify whether you are dealing with a value type or reference type in Go. In Java all primitives are value types and all Objects are reference types so you don’t have to spend any time thinking about it especially in a world of autoboxing. The drawback to the Java approach is if you need performance and memory compactness sometimes you need to use arrays of primitives and not collections. This is something that can definitely trip up a new programmer. Another problem is if autoboxing isn’t handled correctly it is possible for a NullPointerException to be thrown which could also be confusing to a junior developer.

In Go you have to explicitly thing about whether you are passing a value to a struct or a reference to it. Arrays and slices and maps are automatically reference types but any of your structs you have to explicitly declare if it is a pointer or a value type. For example

type struct Person {
  Name string,
  Age uint
}

pointerToPerson := &Person{Name:"Jeff", Age:41}

valueTypePerson := Person{Name:"The Doctor", Age: 904}

If you want to use pointerToPerson above you have to dereference it with the *. If the pointer is nil you will panic if you dereference it. On the other hand the value type if you don’t initialize it goes to default values so the default for the string is an empty string ""and the default for the integer would be 0.

Concurrency

Concurrency is where Go both shines against Java and feels lacking at the same time. Let’s talk about what is great if you want to call a function concurrently it is amazing you simply do:

go handleData() and handleData() will be executed concurrently. The great thing about go calls is they are extremely lightweight. When I read about project loom where they want to bring fibers and continuations to Java. Because they are much lighter weight than a thread in Java you can have thousands of them without a performance issue. It is my understanding that their is a threadpool underneath that executes your different go routines I think similar to an executor in Java with runables, but again I think the go routines are using much less memory than a Java Runnable.

This kind of light weight concurrency seems to be the direction everything is going whether it is the node system of events and async callbacks or even the reactive movement going through spring. Everyone is trying to execute more things concurrently with a very small thread pool.

There is much more ceremony in a Java Runnable or Callable. First I have to implement an interface and then put my code in a specific method. Then I need a Thread or an Executor on which to execute the code. If it were a Runnable and I needed a return type I would need to pass in some sort of concurrent collection to safely send the data back to the other thread in, or I could return it directly in the callable when I get a future out of that callable that can give me the return value.

Java definitely has a ways to go to catch up to go with the ease of concurrency, but the types are much richer for concurrency in Java so with java.util.concurrent.* one has access to about anything you need. In Go you pretty much just have channels to safely pass data between separate Go routines this again has that simple feeling of C where you are building everything up from primitives vs Java where the libraries are much richer.

Tooling

Here in you can tell how young of a language Go is. To me the tools feel like going back in time to around 2005 in Java. The debugger (delve) is primitive and I often find it not stopping at my break points or not showing me the values of all of my variables. I remember using JBuilder and Visual Cafe and some of the early Java IDEs and having similar issues of the debugger just not working that well. I find myself using printf statements to debug again which feels like going back in time 20 years.

IDE support seems decent though otherwise (if you throw out the debugging issues). I am using IntelliJ Ultimate with their Goland plugin and find myself very productive in writing code. It is the same IDE that I know and love from my Java work, and the understanding of the syntax in general seems to be great. I hear that VSCode is also pretty good for writing Go.

Enterprise Features

This is a small point but one that I just hit in the last week or so and that is that the language lacks support for batching SQL statements. Given that Java is such a strong enterprise language I just assumed that would be built into Go as well, especially given that the language is 9 years old, but maybe most of the internet type software being built isn’t using batch operations to slam a lot of data into a database at once.

Conclusion

At this point I still think Java is a more enjoyable language to program in. I like the higher level you can operate at with things like the stream api, and I like how opinionated Spring is. I think that saves you time setting up a new service and leads to consistency when dealing with new code that you haven’t seen before. Go concurrency model feels like it could be more powerful which is why there is that Java project loom to bring fibers and continuations to the language. The value types are an advantage and result in Go using very little memory compared to a typical spring project, which is again why in the Java world they are working on bringing value types into the language. Go is definitely powerful and fast and effective at what it does. If I were still a C programmer I would probably love it, as it feels like C without all the annoyances. As it stands now I can use it, and it works well, but I am not yet passionate about the language.