Code Clash: Unveiling the Pros and Cons of Go vs. Java — A Deep Dive into Programming Powerhouses!

Gayan Perera
6 min readFeb 7, 2024

--

Source: https://unsplash.com/photos/black-remote-control-on-red-table-6sAl6aQ4OWI

In the ever-evolving realm of programming, the clash between languages is inevitable. So let's look at some differences in both of these popular languages in the realm of Microservices.

Let's take an example and write a small microservice that reads data from a MariaDB database and servers it using a RESTful API. For this, we will use the MariaDB sample database employees (https://dev.mysql.com/doc/employee/en/)

Springboot version

Following you will find the Controller class, Response DTO class, Application class, and Application properties which sets the MySQL connection string.

package org.gap.medium.techblogs.employeeportalsb;

import java.time.LocalDate;
import java.util.List;

import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping(path = "/employees")
public class Controller {
private JdbcClient jdbcClient;

public Controller(JdbcClient jdbcClient) {
this.jdbcClient = jdbcClient;
}

@GetMapping
public List<Employee> findJoinedAfter(@RequestParam("after") LocalDate after) {
return jdbcClient.sql("""
SELECT emp_no AS empNumber, first_name AS firstName, last_name AS lastName, hire_date AS hiredOn
FROM `employees`
WHERE hire_date > :after
""").param("after", after.toString()).query(Employee.class).list();
}
}
package org.gap.medium.techblogs.employeeportalsb;

import java.time.LocalDate;

public record Employee(String empNumber, String firstName, String lastName, LocalDate hiredOn) {
}
package org.gap.medium.techblogs.employeeportalsb;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class EmployeePortalApplication {
public static void main(String[] args) {
SpringApplication.run(EmployeePortalApplication.class, args);
}
}
spring.datasource.url=jdbc:mysql://root:${DB_PW}@${DB_HOST:localhost}:3306/employees

Go version

Following you will find the go file which implements the full API.

package main

import (
"database/sql"
"fmt"
"net/http"
"os"
"time"

"github.com/gin-gonic/gin"
"github.com/go-sql-driver/mysql"
)

func main() {
db, err := initDb()

if err != nil {
panic("Error connecting to the database")
}
defer func() {
if err := db.Close(); err != nil {
panic(err)
}
}()

db.SetMaxOpenConns(50)
db.SetMaxIdleConns(20)
initRoute(db)
}

func initDb() (*sql.DB, error) {
var HOST = os.Getenv("DB_HOST") + ":3306"
var PW = os.Getenv("DB_PW")
fmt.Println("Using database host ", HOST)
cfg := mysql.Config{
User: "root",
Passwd: PW,
Net: "tcp",
Addr: HOST,
DBName: "employees",
AllowNativePasswords: true,
}
return sql.Open("mysql", cfg.FormatDSN())
}

func initRoute(db *sql.DB) {
gin.SetMode(gin.ReleaseMode)
router := gin.New()
router.Use(gin.Recovery())
router.GET("/employees", func(ctx *gin.Context) {
afterDt, err := time.Parse(time.DateOnly, ctx.Query("after"))
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid after date"})
return
}
result, err := findHiredAfter(db, afterDt.Format(time.DateOnly))
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("server error : %v", err)})
return
}
ctx.JSON(http.StatusOK, result)
})
router.Run("localhost:8080")
}

type Employee struct {
EmpNo string `json:"empNo"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
JoinedOn string `json:"joinedOn"`
}

func findHiredAfter(db *sql.DB, after string) ([]Employee, error) {
rows, err := db.Query("SELECT emp_no,first_name,last_name,hire_date FROM `employees` WHERE hire_date > ?",
after)
if err != nil {
return nil, err
}
defer rows.Close()

var result []Employee
for rows.Next() {
var emp Employee
rows.Scan(&emp.EmpNo, &emp.FirstName, &emp.LastName, &emp.JoinedOn)
result = append(result, emp)
}

return result, nil
}

Comparison

Looking at the code we can see that the Java version has few lines of code compared to the Go version of the same service.

Let's look at the size of binaries

# Go binary
11163922 - employee-portal

# Java jar file
25951588 - employee-portal-sb-0.0.1-SNAPSHOT.jar

# Java native binary
91276352 - employee-portal-sb

From this, you can see that the Go version of the binary has a smaller size.

Let's look at how much memory they take

# Java JVM startup (kb)
211024 /usr/bin/java

# Java JVM after few requestes (kb)
439280 /usr/bin/java

# Java native startup (kb)
107248 ./target/employee-portal-sb

# Java native after few requestes (kb)
218976 ./target/employee-portal-sb

# Go startup (kb)
12304 ./employee-portal

# Go after few requests (kb)
86768 ./employee-portal

From this, it is evident that the Go version of the binary consumes less memory both at startup and during service for consumers. However, it’s important to note that this is a straightforward comparison and does not consider factors such as handling bursting requests and the capabilities of each runtime’s garbage collector.

Let's look at how fast they start

# Java JVM
java -jar ./employee-portal-sb-0.0.1-SNAPSHOT.jar 3.94s user 0.22s system 213% cpu 1.951 total

# Java native
./employee-portal-sb 0.08s user 0.02s system 33% cpu 0.317 total

# Go
./employee-portal 0.01s user 0.01s system 7% cpu 0.208 total

As you can see the the Go is faster and leaner at its startup compared to Java alternatives

Let's look at how much it takes to compile each service

# Java JVM
mvn clean package --offline 8.92s user 0.73s system 204% cpu 4.719 total

# Java native compilation
mvn -Pnative native:compile --offline 411.89s user 10.37s system 563% cpu 1:14.93 total

# Go
make build 0.16s user 0.28s system 148% cpu 0.297 total

As you can see Go compilation is blazing fast. And that's a native binary that it produces. As you can also see the Java compilation is done offline mode so that no dependency fetching was involved.

Let's look at developer experience

When looking at developer experience let's try to break it down into a few factors

  • Code readability — if considering readability, Java code is easier to read provided that the developer knows what the magic annotations are doing. The configuration file keys are only found by reading documentation and regardless you want to configure it or not you end up needing to provide that value by choosing one of the mechanisms provided by Springboot. Now this doesn’t mean you can do something similar to Go code in Java, But that requires a lot of shoehorning code on top of frameworks like Springboot.
    Now if we look at the Go code on the other hand, a developer can follow how to API is set up and implemented, how request parameters are passed, and how they are validated. But one downside is the increasing number of ifs used for error handling. This aspect can be perceived as both advantageous and disadvantageous, depending on the perspective of individual developers.
  • Development cycle — When working with complex services, you might need to change code while the application is running and apply those changes to try out. In Java, this can be done while in debug mode for most of the changes, but not all. In Go you cannot do this without restarting the service, But given that Go compilation and startup times are less, this might not seen as a critical downside of the language.
  • Ecosystem and Community — Java has a large ecosystem and many leading technology companies/organizations provide different frameworks & libraries. Redhat, IBM, Apache Foundation, Eclipse Foundation, and Go do have a similar community as well, But maybe still not grown up to the same level Java has.
  • IDE Support — Java has many IDEs, to name IntelliJ, Eclipse, Netbeans, and VSCodeJava. Go has similar as well like GoLand and VSCodeGo. Given the complexity of Java and the ecosystem, one might need to rely on a lot of plugins/extensions or pay for a commercial license when working with large projects based on frameworks like Springboot. Given the simplicity of the Go language and the simplicity that is practiced in the Go ecosystem as a mantra, sophisticated IDE feature requirements are very less.

Now you can see the different benefits and shortcomings of each language. But I will refrain from judging which language is better because there is no such thing called

“X Language is better than Y”

My advice is to always choose the language that fits your needs, How to look into the needs and how to evaluate a language will be looked closer in a future story, so stay tuned.

--

--