Assert Your Logic
Assertions are unsexy. You won't read of them in college, witness them in a LeetCode solution, or notice many in production code. Because of this, they're also not much present in training data for AI, and so like you, your AI agents likely won't much care about them. But you, and your AI agents, should care.
In case you're unaware what an assertion is, I've picked out a real assertion I've used in my code below:
class Database():
def __init__(self):
key: str | None = os.getenv("MONGODB_CONNECTION_STRING")
assert key is not None, "Key not found in environment variable"
self._client: MongoClient = MongoClient(key)
# Code continues below...If the key can't be found in the .env file (because it was mistyped, say), the above code will blow up, and inform you so:
File "<...>/database.py", line 6, in __init__
assert key is not None, "Key not found in environment variable"
^^^^^^^^^^^^^^^
AssertionError: Key not found in environment variableAssertions are like the burn one feels when touching a stovetop. They save you from proceeding with dumb choices. They provide immediate feedback: "No, that was wrong. Your logic was wrong. Think and try again." Assertions both 1) clearly mark some wrong assumption someone made and 2) stop (proven illogical) code execution.1
On count one, this is obviously helpful. Debugging has now been made easier. We already know where at least some (or all) of the bug is. If you start using assertions, you'll be surprised at just how often you're wrong about your guesses on how the code works. And you'll also be delighted about how quickly you can resolve the issues. No parsing long-winded stack traces generated by poorly written third-party code hundreds of lines later.
On count two, this is also very helpful in the right circumstances, but it's a little more nuanced, because sometimes you won't want your application to break outright but intelligently recover or just keep running, holding onto the prayer that the logic violation is ultimately non-critical. The thing is that, for the purposes of personal projects, developing quickly, and resolving your LeetCode practice more efficiently, it's (99% of the time) not nuanced. Fail fast. Learn early what's wrong. Make corrections. When the feedback loop is in your control and not in the hands of customers, we don't care about breaking, and we want the feedback loop to be as rapidly circulating as possible. You don't want to wait to learn that you nested several critical flaws atop one another.
This said, failing an application can bite you in certain circumstances. Perhaps you're launching a personal rocket, and it has flight-control software. If the software crashes because of an assertion that proves some bug, your rocket is guaranteed to fail; if you don't assert, then the rocket might succeed in spite of the bug. In this case, it makes sense to ship without assertions enabled (easily done using flags in most popular languages). But that doesn't mean it makes sense to develop, pre-deployment, without assertions.
But what if it's a commercial jet's autopilot? What if when the autopilot crashes, pilots are alerted and they take over? In this case, it might be better to assert that the pitch can't point the aircraft 180 degrees up and to terminate execution early before a stall occurs. And what if a quantitative trading algorithm produces a negative expected value of return?
Things get trickier here, and we might me tempted to use error-handling. Error-handling could catch the aberrant behaviour and intelligently work around it. But error-handling, like assertions, are fairly rare. Error-handling is painful to write, refactor, and work around (when calling functions that use it), so it's often skipped. It costs too much development velocity.
Assertions cost very little to write in the short run, and pay off with superior development velocity in the long run.
