Leepoint Java Reference Notes on the Java language and its standard library.

Fail early

A bug found at its cause is cheap. The same bug found three layers away is not.

Bad input accepted quietly does not go away. It travels, gets stored, gets copied into other structures, and surfaces somewhere with no connection to where it came from. The stack trace then points at the victim rather than the cause.

Checking at the boundary turns a mystery into a one-line fix.

Check arguments

public Account(String id, long balance) {
    Objects.requireNonNull(id, "id");
    if (balance < 0) {
        throw new IllegalArgumentException("negative balance: " + balance);
    }
    ...
}

Two habits worth forming. Objects.requireNonNull throws at the moment the null arrives, rather than at the first dereference, which might be in a different class an hour later. And include the offending value in the message: “negative balance: -40” tells you what happened, “invalid argument” does not.

Validate in constructors especially. An object that cannot be constructed in an invalid state never needs re-checking by its own methods.

Make illegal states unrepresentable

Better than checking is arranging that the mistake cannot be made.

  • final fields cannot be reassigned after construction.
  • An enum cannot hold a value outside its constants, unlike an int “status code”.
  • A dedicated type cannot be confused with another: record Email(String value) cannot be passed where a Username is expected, while two String parameters can be swapped silently.

Do not swallow exceptions

catch (IOException e) { }        // the worst line in Java

This converts a failure into wrong behaviour later. If you genuinely can continue, log it with the exception attached and say why. If you cannot, let it propagate or wrap it with context:

throw new IllegalStateException("Could not read config " + path, e);

Passing the cause preserves the original stack trace. Without it the trail stops at your line.

Fail loudly in development

An assertion or a thrown exception during development is cheap. The same condition tolerated in production becomes corrupted data, and corrupted data is not fixed by fixing the code.

Where it does not apply

At the edges of a system that must keep running. A server handling one bad request, a batch job processing ten thousand rows, failing early means failing that unit early, not the process. Reject the request, record the row, continue. The principle is that the error is detected and reported immediately, not that everything stops.