4 ms·
This is the right move for Go. I have grown to really love Go error handling. I of course hated it when I was first introduced to the language - two things that
by maxwellg 1y ago
This is the right move for Go. I have grown to really love Go error handling. I of course hated it when I was first introduced to the language - two things that changed that:
- Reading the https://go.dev/blog/errors-are-values https://go.dev/blog/errors-are-values blog post (mentioned in the article too!) and really internalizing it. Wrote a moderately popular package around it - https://github.com/stytchauth/sqx https://github.com/stytchauth/sqx
- Becoming OK with sprinkling a little `panic(err)` here and there for truely egregious invalid states. No reason forcing all the parent code to handle nonsense it has no sense in handling, and a well-placed panic or two can remove hundreds of error checks from a codebase. Think - is there a default logger in the ctx?
- deleted 1y ago[deleted]
- masklinn 1y agoThis is just sad. Neither of your supports have anything to do with how dismal Go's error handling is, and neither would be worsened in any way by making it better. If anything they would be improved.
- pas 1y agoEven PHP has better error handling with the levels, and the @ (at operator) to suppress errors at a callsite. Even bash has -e :)
- waynerad 1y agoI've been programming in PHP since 2002 and Go since 2011. At work I work primarily with two large PHP codebases, 1 about ~half a million lines of code, and the other about ~3 million lines of code. I'm porting PHP code to Go and writing some new code, like cron jobs, in Go. There's about 55,000 lines of Go code at this point. What I have learned in this time is: Go is a very good, very high quality, very well designed language. PHP is a terrible, terrible, terrible language -- at least if your mission is creating highly reliable software. If your goal is rapid prototyping of the visual appearance of a webpage, PHP is good for that -- that's what it was originally invented for, back in the 1990s, and it's still the best language for that specific use case. In PHP, if you are looking at a line of code, it's impossible to tell what happens if an error occurs. There's an error_reporting level that can be changed anywhere else in the codebase at any time. So an error might be thrown in the user's face, or not -- depending on the error_reporting level and what the error is. This can also interact with output buffering. (I guess if the "@" sign is present, at least then you know output of an error message is suppressed.) There's a error_handler function that could be set. Oh but it gets worse! Even if you have the entire codebase and can scour every line of code, and trace every possible execution path, you still can't tell what will happen if an error occurs at the line of code you are looking at -- because there are php.ini settings that come into play! This is without even mentioning the try/catch exception handling system, which PHP also has, and that you think is such a good thing. So if an error happens on the line you are looking at, execution may switch to somewhere above you in the call stack. But (at least in theory) if you have the complete source code, you can figure out all the places where that might be. This is an absolute nightmare if you care about reliability. The worst thing you could possibly have in a programming language if you care about reliability is to be unable to tell what happens when an error occurs and reason about the program's behavior. Contrast this with Go. In Go, I can know what happens if an error occurs because it's right there. There's an "if err != nil {..." block that tells me exactly what happens. By porting code to Go, I've been able to increase the reliability tremendously. The thing about go, is that using Go doesn't automatically give you reliability -- you have to learn the techniques and tools in Go that give you reliability. But once you do that, the quality of software you can produce is vastly higher. I've spent the last decade or so learning this. Learning how to structure the code to make maximum use of the compiler's static type checking to catch errors. Making use of the static analysis tools that catch errors. Using a combination of errcheck and staticcheck (mentioned elsewhere in this discussion), you can guarantee every err return value is actually checked and handled. So the oft-cited language flaw of Go allowing you to forget to handle errors becomes a non-issue. Go has built-in support for unit tests and the excellent polymorphism system makes tests easier to make than in PHP. And, getting back to the topic at hand, in Go, I created my own system for handling the err values that get returned so junk errors created by hackers trying to find security vulnerabilities on our server get filtered out, while actual errors that I need to fix get logged and brought swiftly to my attention. Can you do something similar in PHP? In theory, perhaps, but we are to afraid of breaking things to try to massively refactor our ~2 million lines of PHP code in order to do that. So we have various log files and database entries that get clogged with errors, and sometimes we notice errors that we need to fix, but usually we don't. Usually we don't know anything is wrong until we get customer complaints on the Help Desk. Anyway, I'm glad the Go team rejected the idea of changing Go's error handling. Is Go's error handling verbose, tedious, and repetitive? Yes. But it's also unambiguous and gives you total control over how your program responds when an error occurs, which is exactly what you want if you want to write software with the maximum reliability. Don't just take my word for it. Notice how lots of "cloud infrastructure" is written in Go. Kubernetes is written in Go, Docker is written in Go, Terraform is written in Go, etc. Yes, I know some people, like Facebook, have managed to write decently reliable code in PHP. But I suspect Facebook has thrown massive amounts of dollars and people at the problem. Various other PHP projects, like certain website creation systems that shall not be named, have a reputation for being buggy. Sorry, I couldn't let you say PHP has better error handling without a response. It may be "better" for having more compact, prettier looking code. But it's not better for writing actually reliable software. If you want to write actually reliable software, Go is way better.
- pas 1y agoGlobal settings are easy to check and verify and modern frameworks handle it for you, because there's all these knobs. (I agree it's waaay too many, but that's because there's a runtime and on top of that there's a process manager, and on top of that there's nginx/apache or other reverse proxy.) Sure, someone can write a Go library to wrap every low-level function to make sure there's some error handling, maybe with closures and generics it would be quite okay. In Go if you don't see the error handled you know it's not handled anywhere else. Great? Well, sure .. um, maybe? After all if you want to handle it you need to add error handling there. Consequently your code now is 3x as many lines and ~66% of it is returning errors upward. It's the new Assembly. (I don't think try-catch is good, I think that PHP's error handling is better despite try-catch.) Porting to Go (for reliability or otherwise), why? There are other languages out there! Especially if you spent the last decade learning about compile-time checks. I know that k8s (and tons of now-critical software) is written in Go, and it's not a pretty sight -- and instead of having better abstractions there's NASA-cargo-culting[0]. Linux is written in C. It does not make C a great choice for many reasons. (Go is definitely a better choice than C when it comes to memory safety for example, but I prefer Scala or Rust.) Facebook added their own typing to PHP (and tellingly called it Hack, of course). [0] https://github.com/kubernetes/kubernetes/blob/ec2e767e59395376fa191d7c56a74f53936b7653/pkg/controller/volume/persistentvolume/pv_controller.go#L55 https://github.com/kubernetes/kubernetes/blob/ec2e767e593953...
- bravesoul2 1y agoMe too. I'll take the higher Loc for the greater certainty of what is going on. I thought it was clever in C# years ago when I first used to to grok all the try/catch/finally flows including using and nested versions and what happens if an error happens in the catch and what if it happens in the finally and so on. But now I'd rather just not think about that stuff.
- evertedsphere 1y agorust-style "sum type" errors are values too
- 9rx 1y agoBut, of course, makes the incorrect assumption that <T, E> are dependent variables. The idiomatic Go approach is much more representative of reality. Tradeoffs, as always.
- masklinn 1y agoYou have things the wrong way around. A developer uses Result because T and E are exclusive. If they’re not, they will use something else. And it will be clear to the caller that they are in a rare oddball case. The idiomatic Go approach makes no provision for such distinctions at all.
- 9rx 1y ago> You have things the wrong way around. No. You have not considered what is going on around you. > A developer uses Result because T and E are exclusive. A programming language may be designed around that premise, but that is not the case in the real world (faults in the real world are never binary), and it is certainly not aligned with the design of Go. > The idiomatic Go approach makes no provision for such distinctions at all. Naturally. It logically can't because T and E are both considered to be always valid per the fundamentals of the language. The idioms emerged as an extension of those core principles. As before, other languages may take different view, but Go isn't those languages. That's what makes this changing error handling business so hard. If Go were a completely different language then it would be easy to adopt something like monads, but for the Go we have things like that just don't logically fit and it is not yet clear what does.