Users are creative in ways you never intend. Ask for an age and someone types "banana", leaves it blank, or enters minus five. A well-built program does not fall over — it expects the mess. Defensive design means writing code that anticipates misuse and mistakes, and testing is how you prove it copes. Together they turn a fragile program into a trustworthy one.
Input validation: the gatekeeper
Validation checks that data is sensible before the program uses it — like a bouncer checking each guest at the door. There are several kinds of check, and good programs stack several together:
| Check | What it asks | Example |
|---|---|---|
| Presence | Is anything there at all? | Name box not left blank |
| Type | Is it the right kind of data? | Age is a number, not text |
| Range | Is it within sensible limits? | Age between 0 and 120 |
| Length | Is it too short or too long? | Password at least 8 characters |
| Format | Does it match a pattern? | Email contains an "@" |
Authentication: proving who you are
Authentication confirms a user is who they claim to be. The commonest method is a password, kept strong by demanding length and a mix of characters. But a password can be stolen, so serious systems add two-factor authentication (2FA): something you know (the password) plus something you have (a code texted to your phone). An attacker would need both.
Defensive design in general
Beyond validation, defensive design is a mindset: write clear code with sensible names, add comments so others (and future you) understand it, and above all never trust input — check it every time. Think of a lifeguard who assumes any swimmer might get into trouble, rather than hoping none will.
Choosing test data
You cannot test every possible value, so you pick three kinds. For an age field that should accept 0 to 120:
| Test data | Meaning | Example values |
|---|---|---|
| Normal | Typical, clearly valid | 30, 45 |
| Boundary | Right on the edge of allowed | 0 and 120 (accept); −1 and 121 (reject) |
| Erroneous | Clearly invalid, wrong type or range | −5, 200, "cat" |
Syntax errors vs logic errors
A syntax error breaks the rules of the language — a missing bracket or misspelled keyword — so the program will not run at all. A logic error is sneakier: it runs fine but gives the wrong answer, because your reasoning was off (using + where you meant ×). To hunt logic errors, testers use iterative testing — test, fix, test again — and a trace table recording each variable at every step to see where it went wrong.
Normal: 5 — should be accepted.
Boundary: 1 and 10 accepted; 0 and 11 rejected — these edges catch the most bugs.
Erroneous: −3, 99, and "abc" — all should be rejected with a clear message.
Testing all three kinds proves the validation both lets good data through and blocks bad data.
When picking test data, always include the boundary values — the exact edges of what is allowed. Off-by-one bugs love the edges, so 0, 120, and just outside are where errors hide.