Comment

Comment by travisd

Yeah, this is basically what we do. It ends up a bit more ergonomic to not have explicit result types because union types are pretty easy in Python. For example, we have functions like

    # courses.py
    def create(...) -> Course | ErrInvalid:
and they get used like

    match courses.create(...):
        case courses.Course() as course:
            # We use `course` below.
            pass
        case courses.ErrInvalid() as err:
            # Error conditions are usually translated into early returns
            raise fastapi.HTTPException(
                status.HTTP_422_UNPROCESSABLE_ENTITY, err.message
            )
    
    return APICourseCreateResponse(id=course.id, ...)
It has worked (for us at least) pretty well to reduce the number of unhandled errors that make their way into Sentry. The granularity of the error types depends on the need (e.g., sometimes we just have an `ErrInvalid` that returns a message which gets displayed to the user, but sometimes we'll have ErrInvalidAddress or ErrInvalidTaxCode to be more specific for the context).

And yeah, without nodiscard, we try™ to only do this on functions that return values that the caller will definitely need to use. Also combined with exhaustiveness checks on switch statements by default.

parent travisd 8 days ago

No comments yet

Be the first to share your thoughts.

Live from the official Hacker News API

A modern reader for Hacker News · SvelteKit + Skeleton