7 ms·
Less than I thought I would. I work with a very large Go codebase, and I don't remember the last time I had problems because I needed a stack trace. Just greppi
by tail_exchange 3y ago
Less than I thought I would. I work with a very large Go codebase, and I don't remember the last time I had problems because I needed a stack trace. Just grepping for the error message is enough to show me exactly where it happened.
Still, this doesn't mean that Go does not have stack traces. It does have stack traces for panics, and you can create stack traces by wrapping errors.
- iudqnolq 3y agoI frequently am sad about the kind of Rust error that doesn't have stack traces. Do you not often see something like "file not found" and need to know what file wasn't found? Or do the lowest level go error types carry more context?
- philosopher1234 3y agoIIRC os.Open includes the pathname of the file it’s operating on. But still, if you don’t wrap your errors, you get useless errors which are unlocatable. Wrapping is essential in go. Thankfully the STL has good facilities for adding relevant data (like pathnames) to your wrapping
- deleted 3y ago[deleted]
- tail_exchange 3y agoThe programmer needs to be aware that they will need to provide enough context in case of a failure. One thing I see a lot in Go examples is this pattern: body, err := readFile(fileName) if err != nil { return "", err } If the error returned by readFile is just "not found", it would indeed be very vague. This is still poor error handling, in my opinion, since a lot of the context is lost. Yes, they are "handling" the error, but only enough to stop the linter from complaining. I prefer something like this: body, err := readFile(fileName) if err != nil { return "", errors.Wrapf(err, "readFile(%s)", fileName) } This would result inan error like this: readFile(file.txt): not found This way I get all the context I need to know where the error happened and the arguments that caused the error. If the error happened not because of a function call, but, say, an invalid value, instead of this: if n < 10 { return fmt.Error("invalid argument") } Do this: if n < 10 { return fmt.Errorf("invalid argument n=%d is less than 10", n) } In languages like Java, it feels very tempting to let errors bubble up and then let the stack trace take care of explaining what went wrong, but it is often insufficient and may result in hours of debugging. I feel like Go makes it very easy to add extra context to errors, and if you foster the practice of adding context every time you return an errlr, it will be much richer than a stack trace.
- za3faran 3y agoYou basically end up reinventing stack traces, with all the possible ways context can be missed. This summarizes the language quite well.
- kitd 3y agoIME, it's stack traces that miss all the context. So an error occurred 20 levels down? What was it doing? With what?
- za3faran 3y agoYou can see the path that was taken to get to those 20 levels. Without stack traces, you're left to manually peruse the code base to figure all those out.
- preseinger 3y agostack traces are tools for developers, error messages are tools for users users should not see stack traces
- simiones 3y agoGo error messages are very much also tools for developers, if they are useful in any way.
- preseinger 3y agotrue! of course.
- simiones 3y agoI'm not sure we are in full agreement, my statement was a little ambiguous. I meant to say that, just like stack traces, Go error messages are only helpful to developers, at least 99.9% of the time. More generally, user error messages and dev error messages are just fundamentally at odds, there is no way to have messages that are good for both cases. User error messages should explain what went wrong, and what they can do differently to workaround the issue (if anything). Dev error messages should explain what the code was doing when something went wrong, to help with figuring out what code needs to be modified.
- za3faran 3y agoI also worked on a very large golang codebase, and error traces were sorely missed. Searching for the error string is not sufficient. What happens when the string changes in the master branch, which is different from the deployed version? What about when there are different code paths to get to the same error message? I'm aware that it has stack traces for panics, but those should be rare in practice. Day to day debugging was more tedious in golang.
- tail_exchange 3y agoYou will only have problems with the same error message if they are in the same function, otherwise the wrapped errors will show a different path. It is possible, but they will be close to each other. Stack traces can also point to code that is not in the master branch anymore, so it's not like they are immune from it. In both cases (Java and Go), you can git-checkout the deployed commit and then locate the error. I guess we just have very different experiences. I worked with a large Java codebase in the past, and there is no way in the world I am going back to Java now that I tried Go.
- za3faran 3y agoWhich proves that there is more load on the programmer to ensure that no two error messages are the same. What is this, are we back to programming in C with __LINE__ macros?
- tail_exchange 3y agoI believe the issue you're concerned about is relatively insignificant compared to your perception of its magnitude. But if this is something you are very concerned about, there are many approaches you can adopt in a Go codebase to make errors richer, such as using the "errors" package and a structure logger, which are enforceable at CI time and will give you stack traces.
- za3faran 3y agoHaving worked on several large golang code bases, those issues popped up all the time. Yes we used the errors package, but as with the rest of the language, you end up having to reinvent the wheel poorly and in an error prone manner. Incidentally, that employer built their own golang framework with dependency injection and interception to modify error values, such a huge effort for something that comes out of the box with other languages, and it was still not on par with them.