- Define custom error types as `struct MyError <: Exception` and include a descriptive `msg` field — throw with `throw(MyError("..."))`, not with raw strings.
- Use `try...catch e...finally` blocks — always use `finally` to release resources (file handles, connections) regardless of success or failure.
- Prefer returning `nothing` or a typed `Union{T, Nothing}` over throwing for expected absent values — reserve exceptions for truly unexpected failures.
- Use `Base.showerror(io, e)` to implement readable error messages for custom exception types shown to users.
- Catch specific exception types (`catch e; e isa MyError`) rather than bare `catch` to avoid silently swallowing unrelated errors.
- Use `error("message")` as a shortcut for throwing a generic `ErrorException` — but prefer custom types for errors that callers may want to handle programmatically.
- Use `@warn` before throwing non-fatal issues to give callers context before the stack unwinds.