Let It Crash
"Have you tried turning it off and on again?"
The question itself has become a meme. Yet, it is still worth asking that question and often the solution to a given problem. But why does turning it off and on again even work?
State
Most, if not all, software has some form of state. Be it a player's current coordinates in a video game, a focused element in a web browser, or user input. It's all stored in memory rather than a database. As software developers, we have some form of an idea of what the state of a program can look like. According to that idea, we write code and tests for it.
But developers are only humans and can't predict everything happening in a program. As a result, programs can end up in unpredictable and undesired states. Unexpected errors or function calls with wrong arguments can lead to this situation.
Let's look at the following function that takes in an int and returns a boolean.
bool is_minor(int age) {
return age < 18;
}
The function checks if the passed age is less than 18. Nothing complicated. But what if the function receives a negative number? It's not wrong to ignore it, sure. But it goes against the name and purpose of the function. We should ask ourselves why a function that expects an age received a negative number. Throwing an error doesn't solve the problem. It's a bug. It requires fixing instead of coding around it. If we know that the parameter shouldn't be negative, we can assert it.
Assert
Asserts take a boolean as an argument and check if the argument equals true. If it's not, it terminates the program and prints information to the console. That information usually includes the place where the assertion failed and its condition.
NASA's The Power of 10: Rules for Developing Safety-Critical Code states that there should be at least two asserts per function. Tigerbeetle's Tiger Style mentions it as well. Additionally, Tiger Style says that "the only correct way to handle corrupt code is to crash."
Many programming languages offer a built-in assert function. Some programming languages allow more arguments. As an example, a string to print on a failed condition. If there isn't a function like that, it is usually possible to create one yourself.
Taking our C function from before, we can add an assert to it.
bool is_minor(int age) {
assert(age >= 0);
return age < 18;
}
If the function receives a negative number, the program crashes, and we can find and fix the problem.
To Crash or Not To Crash
Knowing when to return (or throw) and handle errors and when to crash is difficult. Unfortunately, there is no guide on when to choose one over the other. You can't guarantee that everything works as expected. Such is the nature of software. But with asserts, you can improve the quality of your software. And you should.