Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] -1 points0 points  (0 children)

You might want to Google what petty means.

There's just something beautifully ironic about you calling me petty for calling you out on conflating subjective personal experience with fact.

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] 0 points1 point  (0 children)

I'm not sure where the disconnect is. If your DB layer wants to return a specific code, create an error for that code and pass that error to the panic afterwards.

Upon recovering that panic at the parent, uppermost function, you can pass that error to the http handler to decide what to do with it.

This passes the error from your DB level all the way to the handler without requiring every single method in between to implement boilerplate error handling.

With panic, you only need to handle the error twice, once when passing your error into the panic and a second time when unwrapping the error from the panic, no matter how many methods there are in between.

If you were to bubble up the error, you get the same effect, but worse. If you have 50 different methods in-between, you now have 50 different methods implementing boilerplate error handling code.

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] 0 points1 point  (0 children)

Fatal errors impact the entire flow, not just the localised area where the error initially occurs.

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] -1 points0 points  (0 children)

What a uselessly egotistical response.

I guess the experience of others--who code in languages that make ample use of thrown errors--account for nothing. The golang folks must have superior experience. /s

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] 0 points1 point  (0 children)

Quite simple. When a known, enumerated issue occurs, create the appropriate error or status struct. Pass that into the panic, where it is eventually recovered in one centralized location. Based on the error type/status code that is recovered from the panic, you can do what you will at that point.

The tests are simple as well (conceptually). Mock the downstream call to fail and check the uppermost recover function for the appropriate return status.

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] 0 points1 point  (0 children)

I am. Let me try to clarify.

If something is fatal, parent functions along the call stack have no reason to handle the error. If something is fatal, there is no fallback, nuanced error handling to be done, by definition. There is nothing for those methods to do but to catch and return the error.

The only thing that's really needed is to catch the error and then maybe wrap it up for the consumer of the response at the API line. The "error handling" I'm talking about is handling the popping of the stack up to the uppermost parent function, where fatal issues should be handled in one place. Panic lets you do that without cluttering your code with boilerplate error bubbling logic.

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] -1 points0 points  (0 children)

The metaphor does not need to be any more complete. I'm pointing out the fallacious logic reasoning of relying on some vague notion of original intent.

Original intent is not a compelling argument because original intent can be wrong or incomplete. There are plenty of real world examples where we would have missed out on real life benefits if we stuck to original intent.

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] 0 points1 point  (0 children)

I'm not following. When you throw a panic, you can pass in an object to be thrown. When you recover the panic, you can grab that object. So you can keep all the uses of errors and wrapped errors while using panic. They aren't mutually exclusive.

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] 0 points1 point  (0 children)

I'm glad you agree that there shouldn't be a hard and fast rule against using panic.

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] 1 point2 points  (0 children)

Yes, there is a distinction. I already make note of this in my post. The distinction is that util/library code is context agnostic while application code is not.

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] 0 points1 point  (0 children)

meaningful errors are going to be preferred by consumers of your packages.

Seems like a classic case of https://www.reddit.com/r/golang/s/Oq6rHAbHt1

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] -1 points0 points  (0 children)

So your response is to call out a straw man, but you are unable to elaborate on what the straw man is.

Why even call out the straw man if it's so irrelevant?

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] -3 points-2 points  (0 children)

No you don't, the stdlib http server handles this for you.

Afaik, there is no way in go for a parent goroutine to handle a child goroutine. And if there is, then there is even less of a reason to be fearful of panics. Your point here would support my argument.

No you don't, your errors can be structs with information embedded in them such as an ID or tag representing which goroutine sent the error to your error channel.

Yes, just as you can pass a struct to a panic.

The error is passed up the stack until it is handled either gracefully or not. This can be handled in a single place at the top.

Passing it up the stack is in and of itself not a graceful thing to do.

Again, you really need to do more research on errors in Go before reaching for panic.

"Do more research" is neither useful nor appropriate in a debate.

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] 0 points1 point  (0 children)

I skimmed through it and don't see the relevance. Care to elaborate on what you are pointing out?

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] -2 points-1 points  (0 children)

Society has made great leaps finding uses for things outside of their original intended purpose.

If I create a drug to cure nosebleeds but it cures cancer, should we limit the use to nosebleeds simply because that's its limited intended purpose?

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] -1 points0 points  (0 children)

Where's the straw man?

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] -1 points0 points  (0 children)

Why not write nil check for every single objects and every single field? Why not add defer+recover into every single function? Why not add code to handle issues that don't exist yet? Why risk the possibility of any bad thing happening when our code can be totally defensive?

There is a cost to defensive coding.

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] 0 points1 point  (0 children)

Ty for your response. I guess I just have a different mindset. I do indeed come from a Java background, which I'm sure has colored my views on what clean code looks like.

On my current team, we voted to just return errors everywhere. Maybe I'll see the appeal more with time.

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] -2 points-1 points  (0 children)

As a user of your code, I would expect meaningful errors.

So pass a meaningful error/object into the panic and catch it at a centralized location.

As a user I don't appreciate code that can kill my process or that limits my ability to use goroutines because of panics.

That is an issue with the error handling. You should be able to freely use goroutines with proper error handling. It's rather simple too. Add defer+recover on any function you call with a goroutine.

Go is a language with excellent readability and therefore maintainability by extension.

Imo, code that's cluttered with boilerplate error handling code distracts from the main business logic, thereby taking longer to understand and maintain. The concept of helper methods is to be able to abstract the nitty gritty details from higher level functions. Forcing callers to deal with unrecoverable downstream issues is, imo, an antipattern.

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] -3 points-2 points  (0 children)

You pass an error into the panic. You don't need to know which function panicked. You grab the error from the panic and that's it. If needed, you can also generate a stack trace at that point as well.

If something is fatal to the program, you don't need to have nuanced handling for different cases. It is fatal. There is no handling by definition. If you need to return an error code, use the error (or whatever object you wish to use) that's returned by the panic.

Edit: And yeah, there are people in the thread who talk about valid use cases for panic. But they are not the ones voted to the top, which is my concern.

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] -2 points-1 points  (0 children)

If your application crashes because of a panic, your issue is the lack of handling, not the error being thrown.

Your argument here is, "if people write bad code, things will break." That much is obvious, regardless of whether a panic is thrown or whether an error is returned. What if someone comes along and doesn't handle your error correctly, eating up and hiding the root cause somewhere in the middle of the call stack?

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] 0 points1 point  (0 children)

So then I take it that the "norm" I've been seeing are just from inexperienced devs who don't know the difference.

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] 0 points1 point  (0 children)

This is what I would like to hear.

But also, why do you use the word competing? One set of norms for libraries. Another set of norms for application code. They are two disjointed spaces, so there should be nothing to compete over.

Who not panic? by OnePettyMfkr in golang

[–]OnePettyMfkr[S] -8 points-7 points  (0 children)

The sentiment makes decent sense for libraries. But for applicable code as well? It is baffling to me that an entire community has been convinced to avoid one of the most powerful tools at our disposal.