Dealign with errors, from my experience - usually there are two cases: 1. you ether don't care about it, and handle at the top level, like intercept it and show message "Error happens, bla bla". In this case, you don't care about any annotations or error types or codes, whatever will work fine. 2. error handling very important, for reliable servers etc., when you need to know exactly what kind of errors could be thrown by function, and act accordingly, like clean resources in some cases or retry in another. And you need to know exactly what could be thrown by function.
Explicit error return with the result, works very well in this case, as it forces you to explicitly handle all possible scenarios. Using `ErrorCode` will be very hard to use. Say you use `sendData` proc. from "networking library v1", you carefully wrote your code, and handled all the error cases. But after the update to "networking library v2", the `sendData` implementation has been changed, and now it throws one more type of exception. With specific error types the compiler would fail and notify you, with `ErrorCode` you would have no idea that the code is not valid anymore and the server may crash because of new unhandled error type.