The assert Trap: A Java Developer's Cautionary Tale
A couple of years ago, I was assigned a bug: some operation was performed even when validation conditions were not met. A quick glance at the method revealed a peculiar sight:
1private void foo(...){
2 assert ... //validation is here
3 //rest of the logic
4}That’s weird, we’re “asserting” the condition. Right? Wrong!
Until that point, I’ve never seen “assert” used in any of the projects I worked on, so I had to do some digging to understand why the validation was not executing.
What is “assert”?
The Oracle documentation defines it as :
a statement in the Java programming language that enables you to test your assumptions about your program. For example, if you write a method that calculates the speed of a particle, you might assert that the calculated speed is less than the speed of light. … Experience has shown that writing assertions while programming is one of the quickest and most effective ways to detect and correct bugs. As an added benefit, assertions serve to document the inner workings of your program, enhancing maintainability.
Example usage :
1private void setMaxRetries(int maxRetries){
2assert maxRetries>MIN_RETRIES && maxRetries< MAX_RETRIES
3... //perform action
4}When to use it, when not to use it ?
Ok, reading that and seeing the example made me wonder why it’s so rare to see it used. When do I need to use it? And the persistent question, why is it not executing? Well, reading the docs actually provides all the answers.
As for when to use assertions, the short answer is: probably never. But the intended use is:
- Internal Invariants: To check conditions. If false, indicate a bug in your code. For instance, ensuring a private helper method always receives non-null arguments from within your class.
- Post-conditions: To verify that the state of an object is as expected after a method execution.
- Pre-conditions (for private methods): Similar to internal invariants; checking conditions for private methods that are only called internally.
- Control-flow invariants: For example, in a
switchstatement with anenum, usingassert falsein thedefaultcase ensures all enum values are handled.
And for when to not use them :
- Validating public method arguments: Public API inputs should be validated using explicit checks (e.g.,
if (arg == null) throw new IllegalArgumentException();). These validations are crucial for the correct and safe execution of your application and must always be active.assertis designed to be disabled in production. - Enforcing business rules: Business logic validations (e.g., “An order must have at least one item”) are not programming errors; they are expected conditions that the application must handle gracefully, typically by throwing specific business exceptions or returning error codes.
- Side effects: Assertion conditions should not cause any side effects because they might be disabled. For instance,
assert calculateAndSetState();is a very bad idea. - Replacing error handling: Assertions are for internal development-time checks, not for handling recoverable runtime errors.
So what was happening ?
Reading through the documentation, I came across the following statements:
There are also situations where you should not use them:
- Do not use assertions for argument checking in public methods. Argument checking is typically part of the published specifications (or contract) of a method, and these specifications must be obeyed whether assertions are enabled or disabled...
- Do not use assertions to do any work that your application requires for correct operation. Because assertions may be disabled…
And that was the answer: assertions can be disabled and, indeed, they are by default.
When you run a Java application from the command line, assertions are not active unless explicitly enabled. This is a deliberate design choice, reflecting their role as development-time debugging aids rather than production-time safeguards.
To enable assertions, you can run the application passing the flags --enableassertions or -ea.
The Fix:
The fix was very simple. Once I understood what was happening, it was just a matter of replacing the assertion with an if statement that throws a descriptive exception.
Conclusion:
This bug highlighted a crucial distinction: assert is a tool for catching internal programming errors during development and testing, but it is not a substitute for runtime validation and error handling of external inputs or business logic.
The silent failure of the assert in our production environment reinforced a fundamental best practice: never rely on assertions for code that must execute for your application to be correct and secure. If a condition is critical for the application’s functionality or data integrity, it must be validated with explicit if statements and appropriate exception handling, ensuring it runs reliably regardless of JVM flags.