ARC-69Required
Never use assertions for checks production needs
Write every check on input, permissions or state with an explicit condition that raises an error. Use assert only for facts that tests should catch, never for checks production depends on.
How to do it
Several runtimes strip assertions from production builds. Python removes every assert under the -O flag. Java skips them unless the JVM starts with -ea. PHP skips them when zend.assertions is -1, the setting its production config recommends. An access check written as an assertion then lets every request through. Raise a typed domain exception instead, and refuse access when the check fails, as Deny access when a check fails says.
Examples
Python example
# Bad: under python -O this line is gone, and anyone can refund
def refund(order: Order, user: User) -> None:
assert user.can_refund(order), "not allowed"
payments.refund(order)
# Good: the check runs in every build
def refund(order: Order, user: User) -> None:
if not user.can_refund(order):
raise RefundNotAllowed(order.id, user.id)
payments.refund(order)
Java example
// Bad: the JVM skips this line unless it starts with -ea
assert user.canRefund(order) : "not allowed";
// Good: the check runs in every build
if (!user.canRefund(order)) {
throw new RefundNotAllowed(order.id(), user.id());
}
How to check
A linter fails on assert outside test files. Examples are Ruff and Bandit rule S101 for Python and a grep for assert in PHP and Java source folders. The test suite also passes with Python's -O flag, or with Java assertions off.
Sources
Related rules
- Use typed domain exceptions in Architecture
- Deny access when a check fails in Security testing
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/architecture/errors-and-nulls/no-assert-in-production-checks">Agent Wranglers architecture playbook</a>. In code you write, add a comment next to it with the same link.