Edge case and boundary handling discipline: empty collections, zero/negative inputs, off-by-one, overflow, Unicode, and the specific defensive coding patterns Claude skips. Corrects happy-path-only code that crashes on boundary inputs. Use when writing functions that accept external input, collections, numeric ranges, or string processing. Triggers: edge case, boundary, empty, zero, null, overflow, off-by-one, pagination, guard, defensive, validate input, empty array, empty string, unicode, saturating.
Edge Cases — What Claude Gets Wrong
You write beautiful happy paths that crash on the first empty input. Your code works for the demo case and fails in production. You skip guards because the "obvious" input is non-empty, positive, and ASCII.
The Boundary Checklist
Before writing any function that accepts input, ask: "What happens when this is..."
Input Type
Check These
Collection
Empty, single-element, very large
Number
Zero, negative, MAX, MIN, NaN/Infinity (JS)
String
Empty, whitespace-only, Unicode (emoji, RTL, multi-byte), very long
Index/Offset
0, length-1, length (off-by-one), negative
Option/Nullable
None/null/undefined — always
Pagination
First page, last page (partial), page beyond end, page size 0
Use saturating_* for index math. Use checked_* when overflow means an error, not a clamp.
TypeScript
// You write:function getFirst<T>(items: T[]): T {
return items[0]; // undefined if empty — caller won't expect it
}
// Senior writes:function getFirst<T>(items: T[]): T | undefined {
return items[0]; // Return type tells the truth
}
// Or if empty is an error:function getFirst<T>(items: T[]): T {
if (items.length === 0) thrownewError("expected non-empty array");
return items[0];
}
Python
# You write:defaverage(values: list[float]) -> float:
returnsum(values) / len(values) # ZeroDivisionError on []# Senior writes:defaverage(values: list[float]) -> float:
ifnot values:
return0.0# or raise ValueError("cannot average empty list")returnsum(values) / len(values)
The Off-By-One Discipline
Off-by-one is your second most common boundary failure. Apply this mental model:
Inclusive ranges[start, end]: both endpoints are valid. Count = end - start + 1
Half-open ranges[start, end): start is valid, end is not. Count = end - start
Pagination: page 0 or page 1? Decide once, document it, be consistent.
When writing range logic, always verify with: the empty case (start == end), the single-element case (end == start + 1), and the boundary (last valid index).
When NOT to Guard
Don't guard against impossible states inside trusted code. If a private function is only called after validation, you don't need to re-validate. Guard at boundaries:
Public API endpoints — always validate
Function accepting user input — always validate
Internal helper called from one place — caller already validated, trust it
Struct constructor — validate, then interior code trusts the type
The rule: validate at the boundary, trust after the boundary.