5

This question already has an answer here:

I could write an endless amount of debugging code to handle the various components of input for a function, check that the correct data types are used, make sure things are decendants of the proper prototype objects, the data is within a certain value range or set... The list could go on and on.

But also of course I can use documentation comment APIs to generate good documentation on the expected input and usage.

My question is, how far/in-depth should I go as far as debugging/checking input for code users versus expecting them to read the funtion / method's documentation for proper use?

We could say "That's up to whoever's in charge" but I'm interested in best practice for maximum efficiency.

marked as duplicate by gnat, durron597, user22815, Dan Pichelman, Ixrec Sep 24 '15 at 19:35

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 8
    "maximum efficiency": don't forget security. If it's a public API and you expect the users to sanitize the input, things will go very wrong very quickly. – Arseni Mourzenko Sep 22 '15 at 12:56
11

Focusing on writing "efficient" code without actually defining efficiency is a common trap developers fall into.

Efficiency isn't always reducing the number of CPU cycles required to accomplish a task. In fact, for most programmers, in most cases, obsessing over shaving CPU cycles is a complete waste of time.

Where Efficiency = minimizing CPU cycles

The majority of business software contains trivial logic.

Imagine spending 30 minutes shaving a few CPU cycles off your code. Let's just say you had a good day and shaved off a 2000 cycles. Your typical 2GHz CPU will go through 2,000,000,000 cycles every second which means your 2000 cycles saved amount to 0.000001 seconds or 1 microsecond saved.

Your application would need to run that optimized code 30,000,000 times before you break even on your time investment.

In all my time in the business software world, I can't think of one instance where that initial 30 minute investment would have been paid off before I left that role for another job.

Where Efficiency = time saved IN GENERAL

On the other hand, if you define efficiency in terms of time saved in general then your ROI sky rockets.

So, in this scenario, time saved includes (but is not limited to) the following:

  • Your time testing your API
  • API Consumer's time debugging issues with your API
  • Your time debugging issues with your API Consumer
  • Your time handling calls about a brittle and broken API (no, the consumer didn't read your documentation)

With this definition of efficiency, a little bit of defensive programming goes a long way. Making your API fail early and fail hard on bad input data requires very little upfront time and saves you and your consumers' a boatload of downstream time.

The big thing to remember is that your time and your API consumer's time is much more valuable (read: expensive) than CPU time.

Other domains

There are however domains where efficiency can mean minimizing CPU cycles. But, keep in mind that those domains are fewer and fewer nowadays.

Two that come to mind are coding for realtime systems and high volume data analysis.

TL;DR

So, unless you know for sure that you need to shave off CPU cycles, and better approach would be code in such a way that you save yourself and others as much time as possible. In the API world you can accomplish this with clear code contracts and defensive programming.

  • Mobile is a frontier which brings us mainstream developers back to focusing on the CPU-cycle kind of efficiency, but my intention was to ask about development efficiency -- however I see the relevance in you mentioning CPU efficiency as more input checks / debugging logic could significantly damage performance in certain data crunching scenarios, rendering, animation cycles, etc., – Viziionary Sep 22 '15 at 14:34
  • Scientific computing is another domain where CPU cycles (and memory access efficiency) is king. And that domain is only increasing in prevalence now that computers are fast enough and large enough for us to tackle practical problems. So while I agree with most of the answer, your "Other Domains" section forgets a huge chunk of software that isn't going anywhere anytime soon. – tpg2114 Sep 22 '15 at 19:10
  • @tpg2114 a valid point. I meant to make it clearer that my list of other domains was by no means complete, but apparently that got lost in my haste to post. I'll fix it when I get back to my PC. – MetaFight Sep 22 '15 at 19:40
  • "Your application would need to run that optimized code 30,000,000 times before you break even on your time investment." That's less than once every second over a year. I can think of plenty of code that runs often enough to make that worthwhile. – Ross Patterson Sep 23 '15 at 0:52
  • +1 for "Focusing on writing "efficient" code without actually defining efficiency is a common trap developers fall into." That was Knuth's point when he said that "Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered." – Ross Patterson Sep 23 '15 at 0:56
7

There are two sides of the problem.

  • As a developer, you have to sanitize your inputs. Don't trust the users to read your documentation in-depth, and don't trust the users to follow the documentation at all. Security-wise, if it's a public API and you expect the users to sanitize the input, things will go very wrong very quickly. For non-public APIs, trusting the inputs is problematic as well; if your code makes bad things to happen when given an invalid input, it's your code which will be blamed, not the caller's.

    If the input is invalid, throw an exception.

  • As an API provider, you may want to simplify the work of the callers who may not be familiar with your API (and find your documentation too difficult, impractical or boring to read). One way to simplify their life is to provide explicit error messages. This is the difference between:

    throw new Exception("Invalid input.");
    

    which simply means RTFM, and:

    throw new ArgumentOutOfRangeException("price", "The price cannot be negative.");
    

    which gives a convenient way to know exactly what is wrong with the caller's code.

    If the input is invalid and you have enough time to explain why is it invalid, do that in the exception.

    If the input error is easily done and is encountered often, focus on that to either eliminate the possibility for the callers to do the error, or provide a very detailed error message.

6

Of course it depends on the way things are done in your organization, (policy of quality work vs. policy of quick and dirty throw-away work, presence or absence of a culture of respect shown by middleware programmers towards application programmers, etc.,) but in general:

The "best" thing is considered to be to write self-documenting APIs whose usage is as clear as possible with just a brief skim through the interface, and to never expect callers of your API to have memorized, or even to have bothered in any great length with, the documentation. Callers of your API will invoke it in weird ways while examining "what if" scenarios. Callers of your API will invoke it in ways that you never even imagined.

APIs are generally expected to be bulletproof and idiotproof.

And this is a good thing. Why? Because:

A "Null Pointer Exception" or a "Division by Zero" exception thrown somewhere within your code is always considered to be your fault. So, for every single bug in the caller's code, the bug gets nonetheless assigned to you. You have to reproduce it, you have to troubleshoot it, you have to discover that it is their fault instead, and you have to convince them that it is so.

An "Invalid Argument" exception thrown by your code is always considered to be the caller's fault. The bug report goes directly to them. Peace of mind.

Excessive arguments between application programmers and middleware programmers where each is trying to prove that it's the other one's fault are generally indicative of sloppy work on behalf of the middleware programmer.

Edit:

I just noticed the term "maximum efficiency" in the question. It is not clear what you mean by efficiency. Inefficiencies in the development process (quarrels about whose fault it is) can often waste huge amounts of time. Now, inefficiencies in the runtime are generally not an issue, because you can always use assertions, which are only enabled on the debug build of the application. But if you do not want to use assertions, the inefficiencies that stem from hard-coded parameter checking are generally not an issue in modern, multi-core, multi-pipelined, multi-level-cached, multi-gigaherz machines. If someone has an issue, tell them to buy better hardware. This is often considered to also be a best practice.

  • What about situations where input upon which certain computations can be performed without overflow is valid, and input upon which those computations would overflow is invalid? In many cases, the amount of human and CPU work to determine whether a given input would cause some computations to overflow may exceed the amount of work (both kinds) needed for the non-overflow case. – supercat Sep 22 '15 at 19:36
  • 2
    @supercat I don't know what you are talking about. An example might help. – Mike Nakis Sep 22 '15 at 19:53
  • Consider a function which is supposed to report whether a point is within a rectangle defined by the x and y coordinates of its upper-left corner along with its width and height. If rectangles with an X of 1 and a Width of 2147483646 are valid, and likewise rectangles with an X of 2147483646 and a Width of 1, what sort of validation should such a function be expected to perform to ensure that it does not flow an overflow exception? – supercat Sep 22 '15 at 20:00
  • 1
    @supercat A reasonable solution would be to require smaller limits, (say, 1 billion instead of 2.1 billion,) so that no overflows can occur. If that is not an option, then the validation would have to be carried out in 64-bit integers. If there is a problem, I am failing to see it. – Mike Nakis Sep 22 '15 at 20:17
0

As the developer of an API you have to decide and to document what valid inputs are, and you have to decide and document what you do with invalid inputs. You also need to decide what are things that don't work, and what are programmer errors. For example, a path to a non-existing file may "not work", while a null pointer or empty string as the path may be a "programming error". And finally, this should be discussed with the users of the API (unless you are experienced enough to decide without their input).

If possible, detect programming errors (that is errors where you expect someone to change their code) and stop your program. If possible, detect other problems, avoid crashing or doing something bad, and report the problem so that it can be fixed or handled by the caller.

Readable documentation helps making users read your documentation :-)

0

For a public-facing API, here's my rule(s) of thumb:

  • Validate all inputs that can cause unexpected behavior.
  • Validate inputs when you first see them.
    • Don't re-validate inputs in internal helper functions.
  • For inputs that could conceivably come from user input, return helpful error codes / messages.
    • if (date < today) return err_NEED_FUTURE_DATE;
    • note that it's not just err_INVALID_DATE, that doesn't provide enough help.
  • For inputs that likely come from the programmer, raise Assertions/exceptions:
    • if (buffer_size < 1) RAISE("Buffer must be at least one byte");
  • Regarding not validating input in internal helper functions: if it's a dangerous operation: please do re-validate. Security-wise it can be a nightmare when data validation/escaping logic is not near the point of use. Best-case: an auditor/reviewer spends 20 more seconds checking that (all) top-level entry points do validate the data. Worst case: he misses 1 entry point, and you have a security issue. – Sjoerd Job Postmus Sep 22 '15 at 18:15
  • Ah yes, the traditional argument over defense-in-depth vs. the "hard shell around a gooey middle" model. Both sides are right and wrong. – Ross Patterson Sep 23 '15 at 0:58

Not the answer you're looking for? Browse other questions tagged or ask your own question.