What is the fail-fast principle in Java, and why is validating inputs early considered good practice?
answer
- Detect bad input/state early, throw immediately
- Locality: stack trace points at the real cause
- requireNonNull / IllegalArgumentException / IllegalStateException
- Validate in constructors → object is always valid
- Loud failure beats silent corruption
basics
~20 sFail-fast means you check for invalid inputs or bad state right away and stop immediately by throwing an error, instead of letting the program keep running with bad data and breaking later in a confusing place.
solid answer
~40 sFail-fast is the practice of detecting invalid arguments or illegal state at the earliest possible moment and throwing an exception immediately, rather than letting a bad value propagate. In Java you validate at the top of a method or constructor: null checks (Objects.requireNonNull), range checks, and invariant checks throw IllegalArgumentException, NullPointerException, or IllegalStateException before any work happens. The benefit is that the stack trace points at the real cause, near the bad call site, instead of a NullPointerException deep inside some helper a thousand lines later. It keeps objects from entering a corrupt state, makes bugs reproducible, and turns silent data corruption into a loud, immediate, well-located failure. It pairs naturally with immutability: validate once in the constructor and the object is forever valid.
go deeper
Can explain that you should check for null/invalid values at the start of a method and throw, so the program stops at the real cause instead of breaking somewhere confusing later.
Picks the right exception (IllegalArgumentException vs IllegalStateException vs NullPointerException), uses Objects.requireNonNull, and validates in constructors to protect invariants.
Frames fail-fast in terms of locality of failure, invariant protection, and immutability; distinguishes programmer errors (fail-fast) from expected domain outcomes (handle gracefully).
Sets team conventions and tradeoffs: where boundaries should validate, fail-fast vs fail-safe/degraded modes in distributed systems, and how it interacts with API contracts and defensive copying.
## The problem fail-fast solves When a program receives bad input (a `null`, a negative count, a value out of range), it has two broad choices: (a) notice immediately and stop, or (b) keep going and hope. Option (b) is dangerous because the bad value often does not crash *where it entered* — it gets stored in a field, passed to another method, written to a list, and only causes a visible failure much later, far from the real cause. By then the stack trace and the program state no longer point at the actual bug. This is sometimes called *fail-slow* or *fail-silent*. **Fail-fast** is the opposite discipline: **detect invalid inputs and illegal state as early as possible, and throw immediately.** "Fast" means *early in space and time* — at the boundary where the bad value first arrives, before it can spread. ## Key terms - **Invariant**: a condition that must always be true for an object to be valid (e.g. "a `BankAccount` balance is never negative"). Fail-fast protects invariants by refusing to create or mutate an object into an invalid state. - **Precondition**: something that must be true *before* a method runs (e.g. its argument must be non-null). Validating preconditions is the most common form of fail-fast. - **Stack trace**: the list of method calls that led to an exception. A fail-fast exception's stack trace points near the real cause; a fail-slow one points at an innocent victim. ## How you do it in Java Validate at the **start** of constructors and public methods, before doing any real work: 1. **Null checks** — `Objects.requireNonNull(x, "x must not be null")` throws `NullPointerException` immediately with a clear message. 2. **Argument range / value checks** — throw `IllegalArgumentException` for a bad *argument* (e.g. negative size). 3. **State checks** — throw `IllegalStateException` when the *object itself* is in the wrong state for the call (e.g. calling `next()` on an exhausted iterator). The convention is: bad **argument** → `IllegalArgumentException`; bad **object state** → `IllegalStateException`; `null` argument → `NullPointerException` (Java treats null as a special illegal argument). ## Why it pays off - **Locality of failure**: the exception is thrown next to the mistake, so debugging is fast. - **No corruption spread**: a half-built or invalid object never escapes into the rest of the system, so you never have to ask "how did this impossible value get here?" - **Loud over silent**: a thrown exception is far better than silently wrong results (e.g. a money calculation that quietly used a `null`-defaulted-to-zero). - **Reproducibility**: failures happen deterministically at the boundary, not randomly deep in the system. The Java standard library is itself fail-fast: e.g. ArrayList's iterators throw `ConcurrentModificationException` the moment they detect the list was modified during iteration, rather than returning subtly wrong elements. ## The flip side Fail-fast is about **programming errors and truly invalid state** — things that should never happen if the code is correct. It is *not* about *expected* outcomes like "user not found" or "file missing"; those are normal and are better handled with return values or checked exceptions, not treated as catastrophic. Knowing which is which is the skill.
- Which exception should I throw for a null argument versus a negative count?Null argument → NullPointerException (often via Objects.requireNonNull). A negative count is a bad argument value → IllegalArgumentException. If the object is in the wrong state for the call, use IllegalStateException.
- Why validate in the constructor specifically?Validating once in the constructor (with final fields) means the object can never exist in an invalid state — every later method can trust its invariants without re-checking, which is the cleanest form of fail-fast.
saying these in an interview costs you the question
- Thinking fail-fast means crashing the whole application — it means throwing at the bad boundary, which callers can still handle.
- Returning null or a default (like 0) instead of throwing on invalid input — that is fail-silent and hides the bug.
- Validating arguments deep inside helper methods instead of at the public entry point, so the bad value has already spread.
- Confusing fail-fast (for programmer errors) with handling expected outcomes like 'record not found'.