Boundaries & Translation
intermediate14 min readLesson 83 of 180
Translating low-level exceptions with chained causes, and choosing checked vs unchecked deliberately.
Exception boundaries and translation
An intermediate codebase translates exceptions at architectural boundaries instead of letting them leak:
// repository layer
public User findUser(String id) {
try {
return db.lookup(id);
} catch (SQLException e) {
throw new StorageException("user lookup failed", e); // translate + chain
}
}
Rules that hold up in review:
- catch only what you can handle — otherwise translate or declare
- always chain the cause (
new X("...", e)) so the stack trace keeps the original story - don't catch Throwable/Error — OutOfMemoryError is not yours
- don't swallow — an empty catch block is a hidden bug
- low-level types (SQLException, IOException) should not cross into the service layer's API
Checked vs unchecked at the boundary: use checked for recoverable, expected conditions the caller must handle (parse failures); unchecked for programming errors and unrecoverable state (IllegalArgument, IllegalState).