- Use `tryCatch(expr, error = function(e) ..., warning = function(w) ...)` to catch and handle conditions — not bare `try()`.
- Use `rlang::abort("message", call = rlang::caller_env())` for package-level errors — it includes structured metadata and better backtraces.
- Use `on.exit(cleanup_code, add = TRUE)` at the top of functions that acquire resources — it runs even when the function errors.
- Use `withCallingHandlers` to log or handle warnings without aborting execution — unlike `tryCatch`, it resumes after the handler returns.
- Signal custom conditions with `rlang::abort("msg", .subclass = "my_pkg_error")` so callers can catch by class, not by message string.
- Use `tryCatch(..., finally = ...)` for cleanup that must always run regardless of success or failure.
- Validate function inputs with `rlang::check_required()` and `rlang::arg_match()` before any computation to provide early, clear errors.