DEV Community

Cover image for Golang through the eyes of a Java developer - pros and cons

Golang through the eyes of a Java developer - pros and cons

Maciej Raszplewicz on December 16, 2020

Recently I had to learn the Go programming language and now I want to share my thoughts from the perspective of a Java developer. We decided to cr...
Collapse
 
robindiddams profile image
Robin Diddams

I write a lot of Go, I agree with most of your assessment, although I think Go was never really designed to live in an IDE, I use vscode but havent touched the refactor stuff, the Go cli to me is home 🏠.

Also I disagree that returning errors makes the code more unreadable, less pretty sure, but not more confusing, I think if youre used to wrapping everything in try-catches and explicitly defining all errors in and out of your functions then Go's way may seem repetitive, but fundamentally it's simpler, call a function get some value(s) back, theres no extra things popping out at inconvenient times (unless it panicsπŸ˜…), I find this to be a lot easier to explain to beginner developers. Again I wouldn't say it looks better, but I think its pretty clear what's going on.

Collapse
 
alainvanhout profile image
Alain Van Hout

I think the main point is that with exceptions you don't need lots of try-catch blocks all over the place. You only add them where they are relevant, while the rest of the code can be unconcerned with them, which isn't the case when you need to bubble up an error.

Collapse
 
mraszplewicz profile image
Maciej Raszplewicz

That is exactly my point. If I don't need to check/expect an error, I just don't do that and I know the program/thread will crash with a stack trace. If I don't have to write all the error handling and return them to the calling function, my code becomes cleaner and easier to read. Like in this example:

func doJob() []string {
    result1 := doFirstTask()
    result2 := doSecondTask()
    result3 := doThirdTask()

    return []string {
        result1,
        result2,
        result3,
    }
}
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
robindiddams profile image
Robin Diddams

wait so you call functions that could throw exceptions but dont handle them until you find that they do throw? I was under the assumption you would still always check but would just prefer to do that check at a higher level (ie. wrap doJob in some error handling), but if you want to ignore errors just don't check them:

// assuming doNTask returns a string and an error
func doJob() []string {
    result1, _ := doFirstTask()
    result2, _ := doSecondTask()
    result3, _ := doThirdTask()

    return []string {
        result1,
        result2,
        result3,
    }
}
Enter fullscreen mode Exit fullscreen mode

lmk if im not understanding you correctly

Thread Thread
 
mraszplewicz profile image
Maciej Raszplewicz

Of course, it is not that I totally ignore all errors/exceptions, I want to handle them (as you have written) "at a higher level". Probably also I don't always know all the exceptions that can be thrown.

And if I ignore an error like in your example, my program will continue to work i.e. all functions doFirstTask, doSecondTask and doThirdTask will be executed. I want my doJob function to fail after the first error. I can achieve that with panic but it is not a good solution too and not idiomatic.

Thread Thread
 
alainvanhout profile image
Alain Van Hout • Edited

Think of it like this: your code is made up of lots of functions calling lots of other functions that are calling lots of other functions. If something goes wrong somewhere, then it's not necessarily the function right above that one that's best placed to handle the error. A thrown exception allows the error to bubble up automatically until the level on which it makes sense to handle the error.

For example, if I have a complex algorithm that at some point does a division by zero, then the code calling the complex algorithm will need to e.g. use a fallback or notify the user of the error. In either case, it's not the algorithm function right above the error that is able to do anything useful with the division by zero error.

Collapse
 
robindiddams profile image
Robin Diddams

I see what you mean, in cases where I don't want to check and wrap an error that would be convenient

Collapse
 
martinhaeusler profile image
Martin HΓ€usler

Go is a really weird one. I was excited at first, because I'm severely lacking a low-level programming language in my personal portfolio (I can do C if I have to, but I don't like it because it's so blatantly unsafe). But then I tried it and... oh man. The error handling is straight out of the previous millenium (by now, we do have fancy things like, you know, try-catch-finally...) and the language has many awkward choices. Back then when I used it you were still limited to a single GOPATH (I've heard that's different now) and package management (in particular: package version management!) wasn't even close to the reliability and quality I was used to in Maven / Gradle.

My absolute worst experience with Go was when I had to parse some JSON data into a struct, analyze it, update it, and serialize it back. It was such a pain. Oh yeah, and no generics on top.

Go does have some stuff going for it though. Being able to produce an executable for any desired target platform independent of the system you're currently running on is impressive. Build times are quite fast and modularity is built-in from the start. The interface duck-typing is something I can get behind because it's compiler-checked and an interesting approach. And the compiler is really really rigid when it comes to code style - an unused variable is an error. Those things I remember as positives, but overall I'll stay away from it for the time being.

Collapse
 
aminmansuri profile image
hidden_dude

Frankly, I love checked exceptions. I think they are a great feature. They're just misunderstood and maligned by Bruce Eckel who didn't get them.

But they make my code much cleaner because I use them properly.

It's fine if some languages are not as strict with types or errors than others. But that was Java's philosophy and I have no problem with it.

Putting if/else error checking is definitely a step backward.

I guess a better way to do error handling in Go would be to return a "result" object that would have an "value()" method to return the value, or an "ifError()" method to handle the error with a lambda.

But the nice thing about exceptions was being able to largely forget about them and let them trickle up to some handler. But it seems to be a hard concept for some people for some reason. Error codes aren't better.

Collapse
 
bias profile image
Tobias Nickel

Thanks for this article, I was reading it, as js/ts developer, who also know a thing or two about golang. (and usually dislike java).

I can't agree more about the aweful error handling. every second line checking for an error.

Once I was writing unit tests, for an existing project. I was using an sql driver mock. It feeled very cumbersome, not productive. Along the way I did some refactoring. First doing the change I wanted, then recompile and run the test as long as everything got into its place again. It was very much guided by golang. (statically types language). While I did not feel productive, only fixing what the compiler tell me, (can't the compiler fix the code for me),... but it absolutely feeld satisfying in the end. and I was a little proud when that work was done.

Collapse
 
marcello_h profile image
Marcelloh

"Error handling makes the code less readable"
You can make it ja biut more like your example:
result1, err1 := doFirstTask()

Stacktraces are possible

I always refactor by hand because then, it comes out the way I want. (and it's not that much more effort)

Collapse
 
alainvanhout profile image
Alain Van Hout

I'm curious. Refactoring is a completely deterministic process that requires some effort and attention to detail but no creativity. Because of that, it's also easy to make mistakes by forgetting a detail or due to a copy/paste error.

In other words, it's a perfect task to be automated. In what way do you not get it exactly as you want it, when e.g. extracting a variable or a method?

Collapse
 
marcello_h profile image
Marcelloh

Refactoring in my eyes is not only to move parts of a complexer function out of there, but rethink why the function was complex in the first place. This kind of refactoring cannot be automated.
I daily check my code for complexity of functions (automated) and refactor when I spot a candidate, because then, it's still fresh in my memory.

Thread Thread
 
alainvanhout profile image
Alain Van Hout

That sounds reasonable, and obviously not something a computer can do for you.

I tend to use the term 'rework' for that, while sticking to the traditional meaning for 'refactor'. But as long as all parties understand what is meant, that doesn't really matter πŸ˜„.

Collapse
 
kilaka profile image
Alik Elzin • Edited

Love the Java Perspective of the Go language.
It feels like Go is the next step for low level languages (C/C++), but the creators forgot some great high level language features.
This makes Go's target applications to be command line executors and independent algorithms.
If Go had embraced some high level concepts like runtime exceptions, JPA, annotations (Spring boot), generics, we could have written any type of app we wanted, with any large scale team of numerous levels of developers.

Collapse
 
andreidascalu profile image
Andrei Dascalu

You do realize that many of these points have nothing to do with the language itself, right?

You don't have to return errors if you don't want/need it. Second version of your function is ok too. The idiomatic handling is verbose, sure but whether or when you need it is up to you. There are plenty of cases when you wouldn't want to return anyway (eg: from goroutines)

Also, your not handling error example is a side effect of reusing variables. I advise against it on principle and I find it sad that Go allows it.

Goland has shortcomings for such an expensive product but many people prefer VSCode for that. I find significantly better than Goland IMHO. But it's weird to say Go has poor IDE support. Did you try anything not by Intellij?

Why parenthesis are not enforced around ifs? Why would they be? You need a reason to use a syntax element not to not need one. Semicolons also exist but are not enforced unless you have multiple statements on a single line.

I find the error handling tedious but not unreadable. It's actually very readable imho, more so than a catch/throw system where the actual handling may be elsewhere. I look at a file and know exactly what happens to my errors (and I can handle only what I want to handle)

Collapse
 
mraszplewicz profile image
Maciej Raszplewicz

Thanks, for your comment. I really appreciate it, even if you don't agree. It is always possible to learn something new.

First of all, my reception of the language is mostly positive.

I know that I can handle errors in different ways, but it will not be idiomatic, most libraries do return errors, so I will have to wrap functions from some lib into my own. So different way (i.e. using panic) is not that good too...

About not reusing variables - agree, but there are plenty of examples, questions etc. where people do reuse error variables. Maybe they are wrong, but my conclusion is that reusing error variables is idiomatic.

You are right that I haven't tried VSCode (I use it but not for Go programming), now I have. It also has shortcomings... And extracting function refactoring creates worse results than in GoLand - it does not compile.

When it comes to parenthesis, this is what I am used to and find more readable. It is not that important though.

Error handling - my opinion is that it forces you to write less readable code. Catch/throw system without checked exceptions is what I like the most.

We can disagree because we are different, have different experiences etc.

Collapse
 
andreidascalu profile image
Andrei Dascalu • Edited

It's not that I don't agree. In fact, I'm familiar with the ideas as they're nearly the same as what I thought coming into Go from PHP. What I'm talking about is a bit of perspective on approaching a different language (and also a bit about separating what makes a language: aka the qualities of the language itself as separate from the platform, including its tooling and again separate from third party support in the form of IDEs).

Thing is, you should define what "idiomatic" means. From what I infer from conversations, it's mostly "the way of doing things that the language enforces or hints at" which is different than "how most people do it through materials/online howto's". I'm sure you also found that quick stackoverflow solutions and straight-to-the-point articles are hardly a collection of best practices. Reusing (or not) is not part of idiom, in this way. You could say that how most people approach solutions should be considered so (particularly because you'll see this in the stdlib, with the caveat that stdlib itself is a collection of tools and ready-made solutions made by people and hardly perfect) but in that case "idiomatic" loses any value since it's not a benchmark for current best practices and merely a mirror or what the majority o developers chose to do some time ago. Following the idiom in this way precludes progress and I've seen this first hand in PHP: it started as a bunch of useful scripts, then grew into a sort of procedural kind of thing before slowly developing OOP-ish ways. What's considered idiomatic has evolved, even if you'd find plenty of people nowadays that swear by procedural spaghetti code of ye olde days.

Thing is, I also liked the try/catch and lots of other stuff in PHP ... but it took learning a few languages that used different paradigms (ranging from slightly different to "other side of the world" different) to put some things into perspective and reconsider the qualities of what was familiar.

Collapse
 
mkunikow profile image
Michal Kunikowski • Edited

How K8s deals with no generics ... mess or not mess ... πŸ€”

The core team replaced a compile-time language feature that was missing (Generics) with their home-built runtime system. And given the tools at their disposal, they did a pretty good job.

medium.com/@arschles/go-experience...

Collapse
 
mraszplewicz profile image
Maciej Raszplewicz

Generics are probably going to be added to the language in the future:
github.com/golang/go/issues/43651
blog.golang.org/generics-proposal

The proposal was created on 2021-01-12.

Collapse
 
freedom profile image
Freedom

The less parenthesis is more readable, I always find why other languages tend to use lots and Ruby syntax was cleaner. Go has a well-balanced syntaxes of all languages that it can do many area good enough.

func keyword is derived from B language by Ken Thompson.

Collapse
 
maku profile image
Martin

I tried go, but a language without generics is hard to use...

Collapse
 
sharandec22 profile image
Shiva Sharan

I am currently transitioning from JAVA to GO. I feel the same about error handling for sure.