David's Deliriums

A Go developer who also likes Rust. I write here.

So Far in Go

Preamble

I have been writing Go for about a year now. For the most part, it is a wonderful language. For some context, before I started using Go, I used two languages primarily: Ruby and Java. Java was for work and school (the “professional” stuff), Ruby was for personal stuff. It’s hard not to start comparing languages, so I’m going to do that with the two languages with which I consider myself most familiar.

Go v. Java

Comparing Go to Java, Go is a godsend. It is a relief going from wiring up a FeatureDelegateInterface for a new feature, then a DelegateFactory to produce FeatureDelegateImpl and FeatureDelegateTestImpl that use a FeatureDao and finally all get wired up into a Spring Boot controller somewhere Before you say “you don’t have to do that in Java!”, just know most of my experience is with Enterprise Java™ where you very much have to do that to ensure you have a robust™, production-ready™ application.

Java is so…heavy when you get to more than just toy applications. Its inheritance focus can also lead to some spooky behavior. You can do dependency injection with Java - it is a language that makes heavy use of object-oriented paradigms, after all - but look at Go: dependency injection is just how you couple parts of your code. If I have a data type that is stored in a database, I would inject a database connection into any functions needing to access the datastore for the type rather than have it inherit from some database interface. I’ve found this to be more elegant and better decoupled. There’s something elegant about code that looks like this:

# project/dir/typename.go
imports (...)

type Person struct {
    id   int
    Name string
    Age  int
    Job  string
}

func FindPersonByID(db *db.Conn, id int) (Person, error) {
    query := `select * from person where person.id = :id;`
    var result Person
    err := db.Query(query, sql.Named("id", id)).Scan(&result)
    if err != nil {
        return nil, err
    }
    return result, nil
}

I’d show a counter example in Java, but with how I’ve written Java Again, Enterprise-Grade™ Java it would take up a lot of screen real-estate. But look at this Go code - the queries to work with a data type are stored in code related to the struct, it doesn’t care about the database (in this case, it just assumes SQL of some sort), it just needs a connection. You’ve probably set that up somewhere else, maybe in a global “service state”-style struct: maybe even a context struct. Handlers have this context, and they inject what is needed when it is needed.

Go doesn’t quite have inheritance - sure, you can embed structs and interfaces, but that’s a poor approximation. And Go’s interfaces aren’t quite the same as Java’s. You still declare functions/methods and their signatures, but then that’s it in Go. Interfaces are implicitly inherited in Go instead of explicitly. This is something I’m still not sure that I like - I personally prefer being explicit over MAGIC! (which coincidentally is why I like Go’s idea of errors).

That’s another thing I enjoy about Go over Java. If I’m writing Java, I tend to not care as much when there are errors in my code. I just wrap the error-producing swath of code in a try-catch and call it a day - usually handling several types of errors at once. In Go, errors are…different. Yeah, they’re still errors you have to handle, and having things like this littered everywhere can get noisy:

if err != nil {
    fmt.Printf("error! %+v\n", err)
}

But, this is a personal preference of mine, I really do like being explicit when possible. And when I’m writing Go code, it’s hard to catch all your errors at once.

Go v. Ruby

Comparing Go to Ruby, I enjoy the performance of Go. Not just execution - compilation as well.

Now wait, Ruby isn’t compiled!

That’s right, but it can have startup times comparable to a compiled language.

Spending time in other compiled languages (I’ve dabbled in C#, C++ and Swift on top of Java), I had become used to waiting for a minute or two as my program compiled and started up. And I got used to booting a Rails app taking a minute (maybe an exaggeration, but it’s reasonable to expect as a Rails project gets larger and larger). Go’s compilation time is a snap comparatively. Especially for being compiled. I also find great joy in the go run command, it provides an experience similar to interpreted languages. I’ve taken to writing quick, one-off scripts in Go over Ruby simply because I can go run them so easily.

I also find Go’s syntax incredibly expressive; closures are one thing that translates from Ruby very well in particular. I’ve always loved the ability to do things however I want in Ruby, and I pretty much have that with Go. However, Go has the added benefit of a team that doesn’t value expressiveness over everything else. I’m thinking of Ruby’s recent addition of rightward assignment - something that sounds cool, but comes with a lot of overhead just for the sake of making the language “more expressive”. Here’s the issue in their tracker

In case you’re unfamiliar, the current implementation (as of this writing) looks like this:

className.some_method => variable

I mean, it looks nice. I can see where it would be useful. But then I think about one of the ways to define a map/dictionary in Ruby: I know this is “old fashioned”, but it was the style when I first started using Ruby back on 1.5!

dict = { :key_name => "some value" }

And I can see people (newcomers especially) getting confused upon seeing the two syntaxes. I could get into how Ruby’s syntax made me a (according to my employer) good developer, but that’s probably another topic. When it comes to being productive, I feel like I get more done in Go over the course of a day. There are lots of ways to do things, but there are usually only a couple of ways to write the code the way you want to.

If I want to do some absolutely wacky stuff with a language, though…I’ll be hitting Ruby up. Monkey patching in Ruby is stupid easy, and when you take advantage of the fact that literally everything in Ruby is an object (even the lines of code you’re running)…

Postamble

These are just a few things I’ve noticed in my switch to using Go full-time. Overall it is an incredibly fun language to work with, and it offers many things to increase productivity. It has downsides, but for me and the work I’m doing, there isn’t a language I’d rather use than Go. Of its downsides, the only one I notice is the lack of generics - which honestly doesn’t cause me a ton of pain. It can lead to code duplication, but I’ve come to enjoy having specific implementations for things over more abstract, generic implementations. So there it is: Go. A perfectly decent language that I greatly enjoy using.