Description
When dealing with inconsistent internal data (e.g. an out of bounds line number), there are many ways how to proceed:
- Throw assertion failure/BugIndicatingError (which will eventually cause an error telemetry event to be sent)
- Remove inconsistencies and continue (best effort)
Users should never be able to cause inconsistent internal state (instead, user input should be validated for consistency), so by definition inconsistent data indicates a bug that should be addressed.
Throwing is an easy way to inform both the user and the developer that the experienced behavior is not intended, however it usually causes a worse user-experience than trying to continue.
On the other hand, silently removing inconsisties makes it difficult to spot and debug the underlying bug, but often the feature is still mostly useable by the user.
I think we can get the advantages of both approaches while avoiding the disadvantages by
- trying to repair inconsistent state instead of throwing (when reairing is reasonable easy to do)
- making it easy for developers of a feature to spot automatic repairs caused by the feature
- while they develop the feature
- Log with
console.error
- Have an easy way to set a breakpoint for any such issues
- Show a notification in the debuggee or debugger
- Log with
- in production / testing
- Collect error telemetry of such automatic repairs
- while they develop the feature
- making it easy for the team to find bugs that caused inconsistent state
- Show an error notification for insider users so that they can share more context than what telemetry includes (with snooze for 1 week for a particular stack trace, and rate-limiting)
Developers shouldn't write code that depends on automatic repair by design.
E.g. instead of relying on Range.fromPositions(a, b)
swapping a
with b
if b
is smaller than a
, a method Range.fromUnorderedPositions(a, b)
should be used that does repairing as part of its specification.
fromPositions
can still do auto-repairing in case b
< a
, but should consider the thoughts from above to avoid hiding bugs.
Example: