simple but powerful

Go vs. Java: we chose Go, and you should too

Go vs. Java: we chose Go, and you should too

We all know that blissful feeling at the start of a greenfield project. Every decision has been left up to you, and the possibilities seem endless. As you think about how to get started, one question you will probably ask yourself is which programming language you’d like to use.

Should you try a fancy new option or stick to your tried and true stack? For us here at Mooncascade, this question is all too familiar, given the nature of our business and the variety of clients we work with. Eventually, we had to choose between Go vs. Java, two C-family programming languages that are both suitable for server-side application development.

Though Java came about in 1991, Go isn’t a new kid on the block, either. It’s been around since 2009, but lately developers have started giving it a closer look. If we’re to believe the TIOBE index, which measures the popularity of programming languages, Go’s usage has risen fairly steadily since 2016 and a growing need for Go developers has been noted ever since. 

Don’t let the cute little mascot fool you. Go is a serious language that’s capable of heavy lifting in enterprise-level projects. In the end, that’s why we went with it, despite Java’s proven track record. Looking back on our decision now, I’m happy we did. Here’s why. 

Go vs. Java: language, architecture, and code

go vs java: language, architecture, code

The first thing to note when comparing Go to Java is that Go is a compiled language, whereas Java requires the presence of a Java Runtime Environment distribution. Any program you write in Go can be compiled into a binary that you can deploy without the need for any further environmental dependencies.

Go is also a systems programming language. Though this term usually refers to low-level languages like C, which is used to write operating systems, Go can be used to build larger distributed systems connected through a network. The same is true of Java, but since Go produces smaller build artifacts and doesn’t require a separate runtime, it’ll make it easier to distribute components and give you faster startup times. 

When writing code in Go, you don’t need to worry about formatting, either—the standard language tooling will do this for you and there’s only one style to follow. This facilitates consistency and helps avoid time spent bikeshedding over style or worrying about configuring integrated development environments, which you’d have to do in Java. 

Finally, Go is the language that many developers use for tooling. Implementing Go would allow you to dive into the source code of many of the tools you use on a daily basis, like Kubernetes, a widely-adopted cluster orchestration system, or Docker, the popular containerisation software. 

Shared benefits of Go and Java

Go and Java do have some things in common, however. Both languages are strongly-typed. This gives them an advantage over duck-typed languages like Python, because they enable the compiler transforming your source code into machine code to verify every piece of data moving through the system. In other words, Go will help eliminate one class of errors in the programs you build, just like Java would. 

Another feature Go and Java share is garbage collection. With garbage collection, the language runtime will take care of memory management for you. Memory leaks are still possible, but it’s generally harder to be unsafe in this kind of configuration. If you did switch to Go, your team wouldn’t miss out on the memory safety benefits that can be found in other modern languages. 

Go, a language for software engineering

go vs java, a language for software engineering

One of Go’s authors, Russ Cox, once quoted a definition that describes software engineering as what happens when you take programming and add time and other programmers to it. 

In that sense, I’d agree that Go is a language designed for software engineering. It’s simple, easy to learn, and doesn’t compromise on power. Plus, it’s flexible and can be used for a wide variety of tasks. It’s got the corporate support to prove it, too. Google, Cloudflare, and even IBM (through its support of Istio) all use it.

Go is also built to last. In any development team, quick hacks have a tendency to turn into long-running pieces of software. Say a coworker asks you to put something together to solve a small problem. It works, others build on it, and before you know it, your team has been using the same program for ten years. You have to keep these kinds of eventualities in mind. Replacing old code with something new will always generate significant monetary and velocity costs, so it’s important to pick a language that lasts. Go will do just that. 

Efficiency through limitation

Another benefit of Go is that it stops teams from being overly clever. In Java, everything is very configurable and you can even change the behavior of libraries to have certain desired properties. With Go, development is more a matter of convention. And if you ask me, this kind of limitation will lead to a much more efficient use of your team’s resources. 

In the world of developers, there’s a joke about seeing a comment written beneath complex blocks of code that says “here be dragons.” But this is usually just showing off: not only is this kind of complexity hard to maintain, it also closes your team off into a bubble of tribal knowledge. Having to come back to quirks like these a year later or taking the time to explain them to a newcomer will be much harder to do than if you had built something straightforward and transparent from the start.

Go cuts down on this. It restricts elements like generics, which enable flashy cleverness, and forces you to write things as simply as possible. Plus, it makes onboarding relatively easy, with its language specification coming in at only fifty pages. 

Concurrency and Goroutines

go vs java: concurrency and goroutines

Go’s major technical benefit is that it’s built for concurrency. Though Java can build concurrent systems as well, it wasn’t designed to do so as effectively. There’s a joke modelling exercise about Go vs. Java where the Go mascot, a gopher, is tasked with burning a pile of Java manuals. Instead of a single gopher doing this all sequentially, one gopher carries the manuals, another moves them with a wheelbarrow, and a third throws them into the fire. This makes the whole process a lot more efficient.

This kind of concurrency is essential in today’s world. Many of the tasks we accomplish on a daily basis depend on multiple concurrent software processes happening at the same time, whether that’s making a payment in a store or loading a page on a social network. 

One way that Go facilitates concurrency is through goroutines, which are a simplified version of threading. Goroutines aren’t threads (which function at the operating system level) but fibers, lightweight concurrency mechanisms that share resources and schedule work within the language runtime itself. They don’t use memory for meta-information the way OS threads do and they’re only limited by the amount of memory available to your program.

Goroutines could be used for loading info about a user on a social network, for example. In order to have the whole process happen concurrently with other subroutines, you’d only need to invoke the function with the go keyword, like go GetFriends(). Idle goroutines wouldn’t use any resources and you could spawn as many as you’d like within your program’s memory limits.

Channels for message passing

This brings us to another one of Go’s benefits: its channel abstraction for message passing. When you’re building a system that uses concurrency, you have to enable communication between these processes. Traditionally, you’d use a shared variable accessible by either one. Then, to prevent conflicts, you’d have to protect this variable with a mutex, which would tell your system “I’m writing, don’t read” and vice versa, to avoid getting corrupted data. But this is cumbersome to write and there will always be the chance that either an errant memory access or a deadlock causing a faulty piece of code slips through the code review phase.

The solution Go proposes is simple. You have a special type of variable called a channel that you can use to share data between goroutines. You only need to instantiate the channel and pass it along to the appropriate goroutines to enable safe message passing. Go turns the complexities of mutex-based communication into a language feature that enables concurrency and makes the entire code-writing process more elegant.

Familiarity and dependency management

Apart from these powerful features, I’ve also found that I like Go for its familiarity. If you were to ask me how Go feels, I’d say it’s like a combination of Python and C, with benefits from each. There’s an old saying that Python can do anything—Go feels similar, especially with its batteries-included standard library. Plus, it has enforced simplicity like C, which means you can get a lot of power out of a deceptively simple-looking language.

One final thing to note for those thinking of making the jump is that Go’s dependency management has really improved in recent years. Developers used to be hesitant about leaving Java’s ecosystem, which had a proven track record of quality and reliability. Go’s latest dependency management modules are increasingly changing this, and should prove convincing even to its staunchest critics. 

Go: a simple, contemporary powerhouse

go vs java: why choose go

In the Go vs. Java debate, there’s no denying that Go has some catching up to do to be as established in the software world as Java is. It’s also true that both languages are strongly-typed and share some key features like garbage collection, which might weaken the argument that a switch from Java to Go would benefit your team.

But if you ask me, Go’s technical capacities give it the edge over Java, especially when considering concurrency. Goroutines and channels for message passing are essential for building a successful software product in today’s world. Go’s smaller build artifacts and ability to run without a separate runtime shouldn’t be underestimated, either.

I also think that Go’s simplicity, consistency, and durability make it a great workhorse for enterprise-level projects. It’ll help your team write clear, long-lasting code and make onboarding easier as you move forward. 

Become a developer at Mooncascade

We are looking for passionate back-end developers to join our fast-growing team. If you share our mindset about GO and would like to build exciting mobile and web products for international clients, then let us know about you!


Published by Siim Kaspar Uustalu

.Siim Kaspar is a software engineer and technical team lead who has spent the last six years split between various domains and stacks. As a professional, he is passionate about quality, systems architecture and tries to bring order to the complex world of distributed systems. At Mooncascade, he has worked with a variety of different clients ranging from startups to public sector institutions. Contributing with both code and operations knowledge he tries to see the big picture in every client engagement.