Asserts are for programmers to find bugs or situations that should never happen. The program should execute normally if all the asserts are removed. Assertive programming section in Pragmatic Programmer states
Whenever you find yourself thinking “but of course that could never happen,” add code to check it.
It is very easy to take that advise and add asserts to handle every negative case. Most languages provide a convenient way to do this check in debug code, which is typically turned off in production deployments after testing. Asserts are not meant to be used for normal checking (i.e. error handling)
Don’t use asserts for normal error handling
Null checks — Consider a python example where we are going to access a dict.
def create_auth_header(oauth): assert oauth return {"Authorization": "Bearer " + oauth['access_token']}
Assert here does not provide anything additional that the language errors do not provide. TypeError or KeyError (in python) provides more information than AssertionError. Very similar to try and catch , asserts littered all over the code distract the programmer that is reading the code from the business logic.
Validating user input — A good program always validates user input, but this should never be done with assertions. Exceptions are there for this reason. In Python, all asserts can be ignored when running the program in optimized mode. All the user input validations could be bypassed accidentally by another developer.
When do I use asserts then?
Use asserts as a tool to alert you when you think the cause and effect are separated by a lot of code. Ever been in situation where a bug in one piece of code shows up as odd behavior in a completely different module making debugging a nightmare? We have all been there. We try to avoid being in that situation by asking a lot of ‘what if’ questions when we write code. What if the input is null? What if the key that I am looking for is not available? What if the world ended? If the distance between the cause i.e the input is null and the effect i.e system exits with TypeError is not within the function or method or class itself, there is a need for an assert.
Let's take a look at an example where assert might save us a lot of time. This example is taken from Python wiki . Please check the other examples there as well. They are very helpful in understanding asserts.
class PrintQueueList: ... def add(self, new_queue): assert new_queue not in self._list, \ "%r is already in %r" % (self, new_queue) assert isinstance(new_queue, PrintQueue), \ "%r is not a print queue" % new_queue
Here assert is used to check duplicates in a list. It is good to fail fast in this case and sleep well at night knowing that there cannot be any duplicates in this list especially the effect of a duplicate might only be noticeable further down the execution path and the symptoms might not directly point to the root cause.
In summary,
- Don’t use asserts to check inputs if the language can fail fast for you
- Don’t use asserts for user input validation
- Don’t turn off asserts in production
- Do use asserts sparingly when the distance between cause and effect is not immediate and hence reducing the side effects
- Do use asserts to prevent your system from going into an inconsistent or non-performant state due to programmer errors. Assertions are great for runtime (i.e external cause errors). “We didn’t test this case because we never thought we could get here” i.e. things that “should never happen”
Discussion (1)