Assert() is underused: a case for four rules to use it well

Reza Naghibi published a blog post, "Assert(): A Modern How To", on August 3rd 2026. His argument: assert() is bedrock for correct, safe, supportable software, but most implementations are underpowered, and developers are genuinely confused about when to use one, a confusion made worse by implementations that let a single compile-time switch disable every assertion in a codebase. He sets out to fix the confusion by naming four areas where assertions belong: value correctness, operational safety, development, and documentation.

Value correctness is the core case. The pattern is to assert that a value is exactly what the surrounding logic expects to handle, for example asserting a system call's error return is falsy right after the call, or asserting a value from random() is greater than zero before using it downstream. If every possible value is already handled by the logic, no assertion is needed; where it is not, the assertion closes the gap, and a triggered assertion is not a bug but the system working as intended, since it preserves 100% value correctness both before and after the failure by stopping the program rather than letting an unhandled value propagate. He extends this to values that are simply hard to handle gracefully, such as a failed memory allocation, a failed lock acquisition, or a thread that fails to spawn, arguing an assertion followed by exit is a reasonable response given how rare those events are. A further reason for full value coverage: the environment code is tested in is not always the environment it runs in, and subtle differences across versions, APIs, libraries, and platforms can introduce silent breakage that thorough value assertions catch defensively.

Operational safety covers memory access. Well-defined types often get bounds checking automatically, but raw memory pointers and C-style arrays need an explicit bound defined and asserted at each access. He gives arithmetic overflow as a second example: after computing c = a + b, asserting c >= a catches an overflow that would otherwise go unnoticed, unless the surrounding code already has structural limits that make overflow impossible.

Development assertions cover logic and state errors and, once code is properly tested, can typically be compiled out of production, though not always. His example is checking a filename split for an off-by-one error by asserting that rejoining the parsed name and extension reproduces the original filename. He notes development assertions can carry more runtime overhead than simple value assertions, which is acceptable in testing but not desirable in production.

Documentation assertions are a variant of development assertions whose main job is reinforcing the assumptions a piece of logic makes, for a future reader as much as for the runtime. His example asserts that a filename's extension separator was actually found, with a message pointing back to the validation function that was supposed to guarantee it. He argues large, frequently refactored codebases benefit most from this kind of assertion.

On cost, he states that properly used assertions run to just a few CPU cycles per check, reading a local value and skipping a branch, an overhead he calls too small to measure in most applications. He then lays out what an ideal assert API needs: separate production and development variants, where production assertions can never be disabled and development assertions can be toggled easily; dynamic messages that include the actual offending value rather than a generic string; a stack dump printed alongside the assertion message for context; and, as a bonus, the ability to capture and dump relevant application state, context, or metrics at the moment of failure to speed up debugging.

Key facts

  • Reza Naghibi published "Assert(): A Modern How To" on August 3rd 2026, arguing assert() is underpowered in most implementations and widely misunderstood.
  • He names four legitimate uses: value correctness, operational safety, development checks, and documentation.
  • A triggered assertion is framed as the system working correctly: it preserves 100% value correctness before and after the failure by halting rather than letting a bad value propagate.
  • Properly used assertions cost only a few CPU cycles, an overhead he calls too small to measure in most applications.
  • An ideal assert API needs distinct production (never disabled) and development (easily toggled) modes, dynamic messages with the actual value, a stack dump, and optional application-state capture.

Why it matters

The post's core claim is that assert() is a correctness tool developers underuse or misuse because implementations are underpowered and it is unclear when an assertion belongs. Used systematically, he argues, assertions also narrow the search space for static analysis, since they encode explicit invariants a checker can rely on.

Who it affects

Any developer relying on assert-based checks, with the sharpest examples aimed at systems-style code: raw memory pointers and C-style arrays, index-based string parsing, arithmetic that can overflow, and codebases with compile-time switches that can disable assertions wholesale.

How to use it

Assert on every value a piece of logic does not already handle (a failed system call, an out-of-range random() result, a failed allocation, lock, or thread spawn) to reach full value coverage; assert on memory bounds and on arithmetic that could overflow (assert(c >= a) after c = a + b); use skippable development assertions for logic and off-by-one checks, such as reconstructing a parsed filename to verify it matches the original; use documentation assertions to record assumptions for future readers, such as asserting a filename actually contains the separator a prior validation step was supposed to guarantee. An API should keep production and development assertions separate, never let production ones be disabled, carry dynamic messages with the actual value, print a stack dump, and optionally capture application state at failure time.

How solid is it

This is a single author's opinion and practice essay on his own blog, argued through code snippets rather than measurements: the claim that overhead runs to a few CPU cycles is qualitative, with no benchmark numbers given, and no specific language, framework, or real-world incident is named. The Hacker News discussion is modest, 10 points and 11 comments roughly five days after posting.

Risks and caveats

The advice is language-agnostic and illustrated with pseudocode, so applying it requires translating the patterns into a specific codebase's actual assert facilities. He warns that development assertions can carry more overhead than value assertions, so a high concentration of them can slow things down, and that assertions triggering frequently in production is never a good sign, meaning the technique still requires judgment about where the line between necessary and redundant sits.

“A failed assertion is behaving exactly as you intended, it correctly caught an edge case.”

— Reza Naghibi, "Assert(): A Modern How To"