Week 04 · lesson
Validate Before You Trust
Programs do not receive “good data” and “bad data.” They receive bytes, text, numbers, objects, messages, and signals. The program decides what those values mean.
That decision is an input boundary.
A reliable system makes the boundary explicit.
Validation asks whether input fits the contract
Suppose a fictional controller accepts a requested speed percentage:
requested_speed = 65
The intended contract might be:
- type: integer;
- minimum:
0; - maximum:
100; - no missing value;
- no text aliases;
- units: percent of configured maximum.
Without that contract, 65 is just a number.
With the contract, the program can decide whether to accept it.
Type checks are only one layer
This code is incomplete:
if isinstance(requested_speed, int):
apply_speed(requested_speed)
An integer value of 1000000 still passes.
A better boundary checks several properties:
def validate_speed(value):
if type(value) is not int:
return False, "type"
if value < 0 or value > 100:
return False, "range"
return True, "ok"
Now the decision is inspectable.
The important defensive lesson is not the Python syntax. It is the pattern:
RECEIVE
↓
PARSE
↓
VALIDATE
↓
ACCEPT OR REJECT
↓
USE
↓
LOG BOUNDED EVIDENCE
Do not use an input first and validate it later.
Rejecting safely is part of the design
What should happen when input fails validation?
Bad answers include:
- crash with an unreadable stack trace;
- silently substitute a dangerous value;
- expose internal secrets in the error message; or
- continue with partially parsed state.
A bounded response might be:
status=rejected field=requested_speed reason=range
That gives an operator useful evidence without echoing unnecessary data.
Lab: build a strict boundary
Use this harmless local program or trace it on paper if you do not have a Python runtime:
def validate_speed(value):
if type(value) is not int:
return False, "type"
if not 0 <= value <= 100:
return False, "range"
return True, "ok"
cases = [0, 65, 100, -1, 101, "65", None]
for case in cases:
accepted, reason = validate_speed(case)
print(f"value={case!r} accepted={accepted} reason={reason}")
Before running it, predict all seven results.
Then compare prediction to observation.
What the test proves
If the results match, you have evidence that these seven cases follow the documented rule in this implementation.
You have not proved:
- every possible input is handled;
- a downstream component uses the value safely;
- the physical machine is safe at 100%;
- the source cannot change later; or
- the program has no other defects.
Good evidence has a boundary.
Add one failure without making the exercise dangerous
Change the validator so the maximum is accidentally 1000.
Do not connect this program to hardware. Keep the activity purely local or reason from the supplied output.
Run or trace:
100
101
500
1000
1001
Record the behavior difference.
Then restore the intended maximum of 100 and retest.
This is a controlled misconfiguration: you changed one rule in an isolated model, observed the consequence, repaired it, and verified the repair.
Positive, negative, and boundary tests
Your final test set should include:
- positive:
65accepted; - lower boundary:
0accepted; - upper boundary:
100accepted; - just below:
-1rejected; - just above:
101rejected; - wrong representation:
"65"rejected; - missing:
Nonerejected.
Notice how much stronger that is than testing only 65.
Input validation is not authorization
A value can be valid and still be unauthorized.
For example, requested_speed = 65 may satisfy the numeric contract, but the current user might not have permission to change speed.
Keep the layers separate:
VALID FORMAT
≠
AUTHORIZED ACTION
Strong systems usually need both.
Finish the Execution and Input Boundary Record
Submit:
- the documented input contract;
- the baseline state table;
- the branch decision table;
- the malformed or boundary case;
- the narrow validation control;
- positive, negative, and regression retests;
- the observed output or paper trace; and
- one limitation.
A strong concluding claim sounds like this:
In the isolated speed-validation model, the revised boundary accepted the documented integer range 0–100 and rejected the tested out-of-range, string, and missing cases. This does not prove downstream authorization or physical safety.
That sentence is the standard for the rest of CS1337: say exactly what the evidence supports, and stop there.