In my experience there's one approach, which might not necessarily prevent bugs, but helps to reduce their numbers, and does not require much effort. I'm trying to use it whenever possible.
1. Code defensively, but don't spend too much time on handling error conditions. Abort as early as possible. Keep enough information to locate error later. Log relevant data. For example just put `Objects.requireNonNull` for public arguments which must not be null. If they're null, exception will be thrown which should abort current operation. Exception stacktrace will include enough information to pinpoint the bug location and fix it later.
2. Monitor for these messages and act accordingly. My rule of thumb: zero stack traces in logs. Stacktrace is sign of bug and should be handled one way or another.
With bug prevention, it's important to stay reasonable, there's only so much time in the world and business people usually don't want to pay 10x to eliminate 50% bugs. And handling theoretical error conditions also adds to the complexity of codebase and might actually hurt its maintainability.
1. Code defensively, but don't spend too much time on handling error conditions. Abort as early as possible. Keep enough information to locate error later. Log relevant data. For example just put `Objects.requireNonNull` for public arguments which must not be null. If they're null, exception will be thrown which should abort current operation. Exception stacktrace will include enough information to pinpoint the bug location and fix it later.
2. Monitor for these messages and act accordingly. My rule of thumb: zero stack traces in logs. Stacktrace is sign of bug and should be handled one way or another.
With bug prevention, it's important to stay reasonable, there's only so much time in the world and business people usually don't want to pay 10x to eliminate 50% bugs. And handling theoretical error conditions also adds to the complexity of codebase and might actually hurt its maintainability.