Smells: Stop using assert statements! 💩

I often come across code using the assert python keyword to validate state. This is a mistake.

“A mistake?? Isn’t that the whole purpose of the keyword?" - you exclaim

You’re right! Which is why it’s insidious! Take the following statement.

assert invoice.paid

You’d think (and you’d be right) this statement will check the .paid attribute of the invoice object is “truthy” and would raise an AssertionError otherwise.

I get the allure, it’s neat, it’s shorthand, it’s pythonic.

I feel there are three issues with it:

1. The assert keyword can be trivially disabled!#

The assert keyword will be straight up ignored if the Python interpreter is running with performance optimisations.

Imagine this scenario; a developer is tasked with improving runtime performance, and it seems adding the performance optimisation flag is a quick win right?

So they go ahead, and enable it, nothing errors, the program works, fab!

Except, not fab, your business logic which depends on asserts is now being ignored. Imagine the carnage! Payment issues, data integrity issues, sanity issues. This is BAD!

You may not become aware of this subtle issue for weeks, and finding the root cause will make hairlines recede. Will you ever be able to fix the data? Probably not!

“Oh no!" - you say

Fortunately there’s an easy way to prevent this - read on!

2. Truthyness is loose#

True, 1 and "stonks" are all truthy. If the invoice.paid value was "stonks" you’d probably like to be aware of that.

This won’t apply to every situation, but when it comes to important data, you better be damn sure it’s the right type of truthy.

You can do assert invoice.paid is True of course, but devs love to shorthand.

3. AssertionError is too generic#

No personal vendetta against AssertionError but wouldn’t you rather have an InvoiceNotPaid error? Or something actually descriptive?

Parsing the stack trace is far easier if the exception is descriptive. If you have assertions all over the codebase then AssertionError is not helpful.

A better example#

class InvoiceNotPaid(Exception):
    pass
    

def validate_invoice(invoice):
    if invoice.paid is not True:
        raise InvoiceNotPaid

But my codebase has lots of assert statements!#

I don’t suggest ripping them out! But you should add an exception to your code standards to avoid introducing more in the future.

For now, there’s a very simple prevention scheme. At the entrypoint to your application add the following code block as a function:

try:
    assert False
    raise Exception('Assertions have been disabled!')
except AssertionError:
    pass

The block will raise an Exception if the assertion passes, which will only happen if the assert keyword is disabled. In practice this means the application will refuse to start, making it VERY clear something is wrong.

So when should I use assert?#

Test cases are a better scenario for assert, and most of the test suites lean on it.

Fundamentally though the keyword is optional - and I personally prefer to avoid it.

Great, I learned something!#

If you found this useful, or you’d like to flame me 🔥, tweet me @juanjsebgarcia 😘