Hacker News new | ask | show | jobs
by theamk 950 days ago
I think it's kinda the opposite actually... with go, you have to go out of the way to generate those errors, while in Python, most of this is automatic.

In particular, the ReadFile example in Python will raise OSError, and those already include filename and error message. So you'd get the same result with 0 extra lines.

For the second example, the json unmarshalling will not auto-add filename, but its easy enough to do using exception chaining:

    try:
        data = json.loads(bytes)
        process_1(data)
        process_2(data)
    except:
        raise Exception(f"Error while parsing file {filename}")
which will give stacktrace like:

    json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "somefile.py", line 4, in somefile
    Exception: Failed while parsing file.json
Note that's even more detailed than go, with significantly less boilerplate (only 3 lines per function) vs 3 lines per call.

(an anecdote: we've had the team which converted the CLI tool from Python to Golang. The first time they ran the tool, it printed a single line:

    invalid character '"' after top-level value
and that's it. It took a lot of debugging before they could figure out what happened. All because they got used to python doing super-rich traceback automatically, with 0 effort from programmers)
1 comments

This is fine as far as it goes. The problem is that the exception can only report context that it knows about. By contrast, the Go version allows you to include any extra context you want, to make debugging easier. Eg, your error might not just include file reading or Json parsing, but which transaction or customer was involved at the time. You can do with exceptions but you either have to try/catch every statement, or add generic context in one big catch block.

I accept it's largely personal preference, but having used both mechanisms for many years, I find Go best practices for error handling are simple and easy to follow, and results in easily maintainable code, compared to exception handling which doesn't really come with a simple set of best practices, meaning it is often badly put together or added as an afterthought.

maybe there are some best error practices out there, but people don't follow that. As an example, I just went to github.com, got the top-trending go project ("ko"], search for error and arrived at this line in [0]:

    dtodf, err := os.Open(filepath.Join(filepath.Dir(file), "diffid-to-descriptor"))
    if err != nil {
      return nil, fmt.Errorf("opening diffid-to-descriptor: %w", err)
    }
See how they forgot to put filename in the error message? if there is some sort of error with the file, you'll have to resort to strace to find out what the name is... Not to mention that this very function returns json parse errors without context, and the caller "getMeta" calls multiple functions and returns the errors without context as well...

best practices are nice, but fully automatic is even better. The minimum-effort path in Python produced vastly more useful traces than in Go, and unfortunately too many programmers go mininum-effort path.

[0] https://github.com/ko-build/ko/blob/cfc13deeb6417d7e1582f031...