Defensive Programming & Input Validation
Defensive programming anticipates and handles unexpected inputs, edge cases, and failures. Validate inputs, provide defaults, and fail gracefully instead of crashing — making your code robust and trustworthy.
Defensive Programming
- Validate inputs — Check types, ranges, and format before processing
- Guard clauses — Return early on invalid input: if (!user) return null;
- Default values — Provide fallbacks: const name = user?.name ?? 'Guest';
- Null checks — Optional chaining: user?.address?.city
- Boundary checks — Array bounds, number ranges: if (index < 0 || index >= arr.length)
- Fail gracefully — Show meaningful errors, not crashes. Log for developers, message for users
Defensive Programming Code
// Guard clauses — return early on bad input
function processUser(user) {
if (!user) return null;
if (!user.name) return { error: "Name required" };
if (typeof user.age !== "number") return { error: "Age must be a number" };
if (user.age < 0 || user.age > 150) return { error: "Invalid age" };
// By here, we KNOW user is valid
return {
display: `${user.name} (${user.age})`,
isAdult: user.age >= 18
};
}
console.log(processUser(null)); // null
console.log(processUser({ name: "" })); // { error: "Name required" }
console.log(processUser({ name: "Alice", age: "25" })); // { error: ... }
console.log(processUser({ name: "Alice", age: 25 })); // { display: ... }
// Safe array access
function safeGet(arr, index) {
if (!Array.isArray(arr)) return undefined;
if (index < 0) index = arr.length + index; // negative index support
if (index < 0 || index >= arr.length) return undefined;
return arr[index];
}
console.log(safeGet([1, 2, 3], 1)); // 2
console.log(safeGet([1, 2, 3], 10)); // undefined (not crash)
console.log(safeGet([1, 2, 3], -1)); // 3 (last item)
console.log(safeGet(null, 0)); // undefined (not crash)
// Safe parse
function safeParse(jsonString, fallback = null) {
try {
return JSON.parse(jsonString);
} catch {
return fallback;
}
}
console.log(safeParse('{"valid": true}')); // { valid: true }
console.log(safeParse("invalid", {})); // {} (fallback)Tip
Tip
Write functions that do one thing well. If your function name includes 'and' (fetchDataAndFormatAndDisplay), it's doing too much. Split into fetchData, formatData, and displayData for testability and reuse.
Never swallow errors silently. Log, report, recover gracefully.
Common Mistake
Warning
Ignoring linter warnings. ESLint catches bugs before they happen: unused variables, unreachable code, missing dependencies in useEffect. Configure it once, then treat warnings as mandatory fixes.
Practice Task
Note
Defensive coding: (1) Add input validation to a function using guard clauses. (2) Set up ESLint on a project and fix all warnings. (3) Write a function that handles null, undefined, wrong types, and edge cases gracefully.
Quick Quiz
Key Takeaways
- Defensive programming anticipates and handles unexpected inputs, edge cases, and failures.
- Validate inputs — Check types, ranges, and format before processing
- Guard clauses — Return early on invalid input: if (!user) return null;
- Default values — Provide fallbacks: const name = user?.name ?? 'Guest';