79

Often when I write a functions I want to make sure the inputs to it are valid in order to detect such errors as early as possible (I believe these are called preconditions). When a precondition fails, I've always thrown an exception. But I'm beginning to doubt whether this is the best practice and if not assertions would be more appropriate.

So when should I do which: when is it appropriate to use an assertion and when is it appropriate to throw an exception?

3
  • 5
    I think this question should be asked on stackoverflow, though it has probably been asked a dozen times there, so you might already find many answers there.
    – user281377
    Commented Oct 29, 2010 at 9:05
  • 7
    IMHO being language agnostic and theoretical this question is more suitable in this site rather for stackoverflow @user281377 Commented Dec 31, 2019 at 16:03
  • Your question does not make sense to me. Doesn’t your assertion throw an assertion error exception? In my opinion assertions are just a convenience method for checking e.g. preconditions such that the reader can immediately tell that it is not an expected situation. An exception is thrown in any case. Commented Jan 1, 2020 at 16:13

6 Answers 6

81

Assertions should only be used to verify conditions that should be logically impossible to be false (read: sanity checks). These conditions should only be based on inputs generated by your own code. Any checks based on external inputs should use exceptions.

A simple rule that I tend to follow is verifying private functions' arguments with asserts, and using exceptions for public/protected functions' arguments.

5
  • Good point about using exceptions for external inputs. I'd also add outputs to that too - problems when trying to create/write to file/database etc.
    – ChrisF
    Commented Oct 29, 2010 at 10:12
  • 20
    And inevitably you'll find those assertions being triggered in production. +1 for verifying private stuff with asserts! Maybe you could say "use asserts when you are in full control of the inputs"? Commented Oct 29, 2010 at 10:40
  • 2
    In java, at least, you have to typically enable the assertion check with the -ea command line parameter. This means assertions are effectively no-ops, unless you explicitly turn them on. Commented Mar 9, 2012 at 13:11
  • 2
    Then why do I as a user trigger asserts in libraries such as opencv for example through public functions?
    – simplename
    Commented Mar 16, 2021 at 23:00
  • 1
    External input does not mean external to your code, it means external to code (e.g. file system, network, environment variables, signals, out of memory). Arguments passed to your functions are internal to code (e.g. your library/framework code + another developer’s application code using it). It is the responsibility of the caller of your functions to check arguments and raise exceptions if they violate your function’s precondition, not the responsibility of your functions. If you don’t trust the caller, you are not doing design by contract but defensive programming (the exact opposite).
    – Géry Ogam
    Commented Apr 20, 2022 at 8:43
55

Assertions are used to find programming errors. Your programs must work just as well when all assertions are removed.

Exceptions, on the other hand, are for situations that can happen even when the program is perfect; they are caused by external influences, like hardware, network, users etc.

3
  • This is a pretty good way to put it. If the user inputs something incorrect, throw an exception. If the input is correct but something's still wrong, throw an assert. Commented Oct 22, 2018 at 21:38
  • 4
    If the user inputs something incorrect, that should be expected, not an exception.
    – gnasher729
    Commented Dec 31, 2019 at 16:43
  • If it is a good idea to use exceptions for normal control flow is a very controversially discussed topic. Commented Jan 1, 2020 at 16:18
8

Although, I have posted the answer in stackoverflow site, that might be still helpful to post here.

Assertions are used,

  1. When you want to stop the program immediately rather to proceed with an unwanted state. This is often related to the philosophy of the Fail-fast [ 1 ] system design.

  2. When there are certain possibilities of cascading failures (i.e. in microservices) for the first unexpected condition that might lead the application into severe inconsistent or unrecoverable states.

  3. When you want to detect bugs in your system exclusively in the debugging period. You might want to disable them in production if language supports.

  4. When you already know that the unexpected conditions arose due to your internal miss-implementation and external system (i.e the callers) has no control over the unwanted state.

Exceptions are used,

  1. When you know that the unexpected conditions arose due to external systems fault (i.e. wrong parameters, lack of resources etc).

  2. When you know that the conditions can be backed up with alternative paths keeping the application functional qualities still intact (i.e. might work well for another call with proper parameters from the caller or external system).

  3. When you want to log and let the developers know about some unwanted state but not a big deal.

Note: “The more you use assertions, the more robust system you get”. In contrast “The more you use exceptions and handle them, the more resilient system you get“.


[ 1 ] Fail fast - In systems design, a fail-fast system is one which immediately reports at its interface any condition that is likely to indicate a failure. Fail-fast systems are usually designed to stop normal operation rather than attempt to continue a possibly flawed process. Such designs often check the system's state at several points in an operation, so any failures can be detected early. The responsibility of a fail-fast module is detecting errors, then letting the next-highest level of the system handle them.

3

Typical programming practice is to compile out assertions from production/release builds. Assertions will help only during internal testing to catch failure of assumptions. You should not assume the behavior of external agencies, so you should not assert on events from the network or user. Also it is a good practice to write handling code for production builds in case an assertion fails.

For example in C,

int printf(const char *fmt, ...)
{
  assert(fmt);  // may fail in debug build but not in production build
  if (!fmt) return -1; // handle gracefully in production build
  ...
}

Exceptions are meant to be built into production builds. The alternative for exception is returning error and not assertions.

2
  • 5
    I don't think it's a good idea to put graceful behavior behind an assert for the same condition. Inevitably, because the graceful behavior only exists in the production version, the code that was supposed to deal with the graceful behavior will be poorly tested and crash just as bad as, if not worse than, the code would have were it not guarded. Commented May 3, 2013 at 16:37
  • Sebastian Speak for yourself. Most assertions in my code are handled somehow, very few kill the app (and these kill the app in a release build as well). My QA guys don’t like change in behaviour from development to release build.
    – gnasher729
    Commented Dec 31, 2019 at 16:45
1

One problem with asserts for me is that they are disabled by default in Java.

We use a fail-first strategy where the program - which may have been running unattended for years - needs to crash as early as possible to avoid data corruption in case of bad data (on an unexpected form). This is what we use the checking for, and by using asserts we basically risk them not being active.

0

This is language-agnostic but I will make two assumptions, here:

  1. Assertions can either be left out of the build of the program or can be disabled at runtime (the latter is like Java assertions)
  2. You can catch assertions: an assertion will not necessarily kill the program

I basically agree with most of the other answers: assertions should always be used to check for bugs (stating assumptions) and never to check for things that are outside of your own control, like e.g. user input.

So, by extension, exceptions (or general error handling) should handle everything that could go wrong as a matter of course.

But I don’t necessarily think that only assertions need to be used to check assumptions; you might sometimes want to use exceptions for that. Why? Because of point number one: assertions might be disabled. So you might want to use exceptions for, say, stating assumptions that never should be violated, no matter what kind of build or runtime configuration you are using.

So what’s the sweet-spot for assertions? Well, it seems that assertions are enabled and disabled based on two criteria: (1) the cost (performance-wise) of the check and (2) whether one should crash when the assumptions are violated or whether one should do something less intrusive and simply carry on, like for example just log the error. But recall point number two above: we assume that we can catch assertion errors (like catching AssertionError in Java). So let’s for simplicity’s sake assume that we can catch and log (i.e. not crash) assertion errors if we want.(†1) Then it seems that perhaps the sweet-spot for assertions are checks that you might not always want to run based on their cost. So use assertions for things that you might want to sometimes disable. Maybe you for example want to:

  1. Always disable assertions in production
  2. Selectively disable assertion code that has run in production for six months or more (with assertions enabled) and which has been fuzzed in local testing
  3. Disable assertions in production which you are confident that will run the same as when it is run locally or in testing (i.e. the whims of user load and input isn’t that concerning to you)
  4. Never use assertions at all since you are fine with the cost (performance hit) of always checking assumptions (i.e. you don’t need the on/off feature of assertions since they would just always be on)

Or whatever other scheme you want to use.

Notes

  1. Or if you can’t catch assertions then you can implement something like assertOrLog() which crashes when asserts are enabled or logs the error if not:

    // Java
    assert false : errorMessage
    log.error(errorMessage);
    

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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