Blog
Null Is Not An Option: Use Optional Or Fail
Null is billion-dollar footgun with tiny syntax. Modern Java gives better options, and still teams return null like it is 2006.
If absence is valid, model it. If absence is invalid, fail loudly. Do not leave trap on floor for next developer.
Old Style Null Soup
Customer customer = repository.findById(id);
if (customer != null) {
Address address = customer.address();
if (address != null) {
send(address.email());
}
}
Every line asks permission from void. Business rule is hidden under defensive noise.
Worse version skips one check and high-load system starts throwing NullPointerException only for rare imported accounts.
Use Optional For Possible Absence
Optional<Customer> findById(CustomerId id) {
return jdbc.query(...).stream().findFirst();
}
Customer customer = repository.findById(id)
.orElseThrow(() -> new UnknownCustomerException(id));
Now caller must decide. This is good pressure.
Do not use Optional for fields in JPA entities. Do not use Optional parameter for every method. Use it mainly for return values where absence is normal.
Fail For Impossible Absence
Constructor should reject invalid state.
record TransferCommand(AccountId from, AccountId to, Money amount) {
TransferCommand {
Objects.requireNonNull(from, "from");
Objects.requireNonNull(to, "to");
Objects.requireNonNull(amount, "amount");
if (amount.isZeroOrNegative()) {
throw new IllegalArgumentException("amount must be positive");
}
}
}
Now service does not need guess if command is valid.
Pattern Matching Helps Readability
String describe(Object value) {
if (value instanceof AccountId id) {
return "account " + id.value();
}
if (value instanceof Money money) {
return "amount " + money;
}
return "unknown";
}
Modern Java makes type checks clearer. It does not make null safe automatically. You still design absence.
Do Not Abuse Optional
Bad:
Optional<Optional<Customer>> customer;
Also bad: optional.get() because you were too lazy to model path.
Use map, flatMap, orElseThrow, and clear domain exceptions.
Final Rule
Null should be rare and contained at boundaries: database driver, JSON parser, old API.
Inside domain, absence is explicit or impossible. Anything else is delayed incident.