I understand your question as "Should you validate input data?", as opposed to "Should you use Exceptions instead of error codes?".
When accepting input parameters from outside - be it a method with call-parameters, XML input data from a service call or a web-page with user-fields - you basically have two choices:
- make rigorus checks whether the input data match the expected format
- trust your caller to only call you with correct data
Adding checks adds complexity
- the initial developer has to think of all valid (or "possibly invalid") values and add appropriate checks. Ideally this is done at the beginning of the method, though sometimes it has to be done later (e.g. if "valid input value" is "the ID of an actually existing object in the database")
- further developers trying to understand the actual logic of the method, as it adds more lines to investigate.
- runtime overhead to check all assumptions
On the other hand, input validation brings valuable benefits:
- It enables you to make assumptions deeper down in the coding, as they are already verified
- further developers may have more to read, but making assumptions about input data explicit also helps understanding the logic, and that corner cases are taken care of.
- It ensures that no corrupt program states (crashes, data corruption, security vulnerabilities) can occur.
So the choice of adding checks should be done in context where your method is used.
- Is it exposed to untrusted, external data, like web sites or public APIs? Then definitely do thorough validation.
- is it a private method in an often-called inner-loop, protected by outer coding? Then you probably don't want redundant checks. You might want to document your expectations (e.g. JavaDoc), though that's rarely done in practice.
- some languages provide conditionally compiled assertions, which are enabled in debug builds for catching program logic errors, but disabled for performance in productive use
- for libraries you have to decide how much you trust your callers. Better err on the side of caution and add checks, especially if you don't want your library invariants to be compromised. For speed relevant functions, though, you could decide differently (and document it).
- usually a mixture is done: good validation on externally facing methods, some validation on interface levels, very few (often just NullPointerChecks) in the inner workings of a program.
If your question however was "Are Exceptions the right way to signal invalid input data?", the answer is: most likely yes.
Exceptions impose quite an overhead on the runtime performance, but makes reasoning about the program flow drastically easier. This reduces faulty programming (semantic errors), especially as it forces you to deal with them - they 'fail securely' by terminating the program if they are ignored. They are ideal for 'situations which are not supposed to happen'. Also they can transport metadata like a stacktrace.
Error codes, on the other hand, are light-weight, fast, but forces the method caller to explicitely check them. Failure to do so often results in program flaws, which can range from silent data corruption, security holes, to nice fireworks if your program happens to be running inside a space rocket.
One example where I personally would have preferred error codes instead of exceptions is the String.parseInt() method in Java. Getting a non-digit string from an input source (e.g. a user) is not totally unexpected and I have to deal with it in any case - sometimes as simple as using a default value. As a caller, I have no way of checking the data whether it would provoce an exception (short of implementing the validator logic myself), thus generating the exception as part of normal program flow and using try-catch-blocks is the only choice here.
Please note that error codes could be both in-band and out-of-band:
- In-band: a special (magic) value is declared to be a marker for the error condition. Often null or -1 is used here, e.g. in string-search-methods
- Out-of-Band: a separate value is returned, indicating 'no error' (usually 0 or null) or the specific error. This can be done either as the / an additional return parameter (for languages supporting multiple return values - e.g. Googles GO supports this especially for errors) or as a requirement to call a special "getError()" method.