DEV Community

Discussion on: 10 Reasons NOT to use Go for your next project

 
dominikbraun profile image
DB • Edited

Handling the error for every call becomes a noisy affair of testing every call or wrapping everything you can. In Java we can just move that error handling up the stack. The user doesn't care that method X or Y failed. They care that that the operation as a whole failed which is why we have generic handling.

This is exactly what Go wants to avoid: Propagating an error up to a caller that has nothing to do with the actual error. Go wants you to handle errors immediately where they occur.

Other than that, your approach comes with a problem:

int myFunction() throws Exception {
    int a = calculateThis();
    int b = calculateThat(a);
    int c = calculateThose(a, b);

    return calculateResult(a, b, c);
}
Enter fullscreen mode Exit fullscreen mode

One of those functions throws an exception. Which one?

I've been a Java Developer myself and think that Exceptions are a good, but far from perfect approach. Rust does this better, and Go does it more verbose but also more explicit.

Thread Thread
 
codenameone profile image
Shai Almog

The one the stack trace points at. You would literally see the name of the problematic function and the line number in the stack. There's no chance that you will accidentally "forget" to log an error properly. No discipline required by the developer.

Thread Thread
 
valeriavg profile image
Valeria

As far as I know there are several types of exceptions in Java, we are referring to the ones that are not possible to check at compile time. Web applications deal with a lot of user input. Bad, malicious, unfiltered user input. That means that parsing a JSON string would fail, that some arrays would come with unexpected lengths and so forth. Go is made for web and there is no surprise it was designed this way, if you think about it. You don't want an HTTP server to hang the request, crash and restart, because someone sent some hacky request. You want to send a proper error response to the user. And often, when it comes to database issues, you also want to obfuscate the actual errors to avoid leaking your schema. With try/catch you need to either wrap every single call with it or create some horrendous upper level wrapper.

Thread Thread
 
codenameone profile image
Shai Almog

The most popular framework for Java developers (by far) is spring. It lets you define exception handlers and comes with default ones that would show the right response error if an exception makes it all the way up.

In Go you will either crash or completely miss the error (in case you didn't check) at which point the failure can cascade to something worse.

Try/catch is the exact opposite of wrapping every single call. I'm assuming you saw horrible code from people who just don't know how to use exception handling. E.g. In Spring boot I can define the exact exception types at the top level and then just throw whenever I want in the code. This will translate to the right error code to the user with all the details embedded into the response seamlessly. You can write any code you want and don't need to "deal" with errors for the most part. This is easy, efficient, maintainable, secure... Literally no downsides when compared to your approach.

My guess is that you saw some horrible try/catch nesting some novices do sometimes especially for older Java code where close() used to throw an IOException and we didn't have try with resources. The syntax is MUCH better now and the powerful capabilities you can layer on top of this (tooling, tracking etc.) is at a completely different level.

Thread Thread
 
valeriavg profile image
Valeria

You are missing the point. You CAN'T miss an error in Go. You are forced to do something about it. If you chose to ignore it, you do it at your own risk this way:

result, _ := doSomething()
Enter fullscreen mode Exit fullscreen mode

You need to handle the error explicitly. Go won't compile if you do this:

func doSometing() (int, error){
 return 42, nil
}

func main(){
// won't compile, you need to handle the second parameter
 result:= doSomething()
}
Enter fullscreen mode Exit fullscreen mode

And you are forced to write your functions this way, because the whole standard library is written like this.
You don't need a framework for it, you don't need discipline, you don't need to remember and thus you can concentrate or just writing the code.

And if you DO want try/catch "exceptions" you can recover from a panic:

// https://gobyexample.com/recover
package main

import "fmt"

func mayPanic() {
    panic("a problem")
}

func main() {

    defer func() {
        if r := recover(); r != nil {

            fmt.Println("Recovered. Error:\n", r)
        }
    }()

    mayPanic()

    fmt.Println("After mayPanic()")
}
Enter fullscreen mode Exit fullscreen mode

That's the whole point. Go produces fast apps, clean code and compiles fast. And Go alone is enough, you don't need a framework or even a third party library to build at HTTP server.

Thread Thread
 
codenameone profile image
Shai Almog

I hope this is an enjoyable debate to you. I don't want to come off "trollish" as in "my language is better than yours" sort of way. I think Go has a lot of interesting strengths, I just think the lack of exceptions isn't one of them. If this debate feels "iffy" let me know and I'll delete my comment with no hard feeling ;-)

You can miss an error in the same way you can miss handling an exception in Java. Miss an if or don't do a proper return when an error occurs and let it cascade. You're forced to call into the standard library like that but third party APIs don't need to follow that convention. Just like 3rd party Java APIs often convert checked exceptions to runtime exceptions.

Panic isn't try/catch. It's setjmp + longjmp. That's not practical at scale.

Go produces fast apps

So does Java, Rust and even C++. That's not a big deal.

clean code

We can debate that. I think that code that has to check for errors all the time is funky but as you clearly said, you disagree.

compiles fast

So does Java and most languages nowadays.

And Go alone is enough, you don't need a framework or even a third party library to build at HTTP server

This is a pretty bad claim. Your presenting lack of choice and diversity as an advantage?

Notice that Spring isn't an HTTP server. It's an application engine that includes everything from IoC onward and integrates dynamically with dozens of frameworks. That's a strength IMHO.

Thread Thread
 
dominikbraun profile image
DB

The one the stack trace points at. You would literally see the name of the problematic function and the line number in the stack. There's no chance that you will accidentally "forget" to log an error properly. No discipline required by the developer.

Absolutely. However, when reading/browsing the source code, you don't know which methods throw which exceptions, where those exceptions are handled and what the control flow looks like. You immediately see what's happening when looking at Go (or Rust) source code - the control flow is very explicit and obvious. But this probably is a matter of personal preference.

Thread Thread
 
valeriavg profile image
Valeria

Oh my, no this isn't an enjoyable debate.
Your claims are based on your subjective perception and, if I'm honest, quite disconnected from the reality.
Go promotes diversity and code sharing much better than a lot of languages. You can literally use any repo as a package. Therefore there isn't a monopoly over http like it is in case of Spring, but plenty of options, yet you are not obligated to use them.
Leave the comments, there was a lot of examples readers might find useful, which was ultimately the goal of the article.

Thread Thread
 
ecyrbe profile image
ecyrbe

I'm programming in rust nowadays and like Go, error handling is explicit.
Rust has syntactic sugar with the "?" keyword to forward errors and let the caller handle the error. Does Go have the same feature ?

Thread Thread
 
valeriavg profile image
Valeria

Nope, Go doesn't have that.

Both Rust and Go use the return value as an indicator of an error and enforces handling it on spot, but that is the only similarity.

Go's error interface is defined by the presence of an Error() string method and cannot do anything, but becoming a string.

Rust's Result has handy methods like unwrap or expect, along with the sugar.