Convened by Diomidis.
- Recovery methods
- Exception classes
- Testing
- Language approaches
- Compiler help
- Static analysis tools
- Fail fast; give up and let some other part handle the problem at a higher level
- Retry. This can be dangerous, unless the underlying calls are idempotent
- Prompt the user for a correct reply
- Return a cached result
Note: The UX part is difficult to get right, but must be designed from the beginning to account for all possible failure modes.
- Utilize the Java 8 CompletionStage API
- Design your architecture so that clients can be able to recover from failed service calls
- Pass the error at a higher level where it can be handled in a more inteligent manner. For example, you can rethrow I/O exceptions as runtime exceptions. However, this looses the exception's type.
- A better approach involves the use of Lonbok's sneaky throwing annotation, which allows you to declare that a method's exceptions are handled at a higher level, and thus avoid the boilerplate code. An exception handling policy can formalize how exceptions are handled using this mechanism.
- At lower levels, handle all exceptions as unchecked. This avoids errors that
developers commit when they encounter an exception and they don't know how
to handle it (e.g. they return
null
or-1
).
Note: Asynchronous code complicates the picture.
- Use the WireMock library to simulate HTTP-level failures
- Use Linux tc (traffic control) to simulate delays and increased latency
- In a microservice environment inject failures through a container sidecar
- For unit tests, you can pass around a custom
FileSystem
object (to simulate file system failures) or aClock
object to simulate the advancement of time.
Note: In unit tests, avoid looking at the exception's error message.