Week 05 · lesson
Parsing Is a Boundary
A parser turns one representation into another.
Text becomes numbers. JSON becomes objects. CSV rows become fields. Bytes become protocol messages. Configuration becomes runtime state.
Every conversion is a point where assumptions become behavior.
Follow the transformation
Imagine this input arrives as text:
battery_percent=73
A program might perform:
raw bytes
↓
UTF-8 text
↓
split on '='
↓
field name + field value
↓
convert "73" to integer 73
↓
validate 0..100
↓
store current battery state
A failure at any step produces a different class of problem.
If UTF-8 decoding fails, that is not a range error.
If the delimiter is missing, that is not a numeric conversion error.
If conversion produces 730, that is not a field-name error.
Precise diagnosis begins by naming the failed stage.
Parsing and validation are separate jobs
Consider:
raw = "101"
value = int(raw)
Parsing succeeds. The string is a valid integer representation.
But if the contract is battery percentage, validation should still reject 101.
So:
PARSE SUCCESS
≠
CONTRACT VALID
That distinction prevents sloppy error handling.
Serialization creates the reverse boundary
When a program sends structured state elsewhere, it serializes it.
For example:
state = {"battery_percent": 73, "enabled": True}
may become:
{"battery_percent":73,"enabled":true}
The receiver then deserializes the message.
If both sides share the same contract, the exchange is predictable.
If one side changes the schema silently, the receiving side may fail or — worse — interpret the data incorrectly.
Versioning makes change visible
One simple strategy is a schema version:
{
"schema_version": 2,
"battery_percent": 73,
"enabled": true
}
Now the receiver can explicitly decide which versions it understands.
A version field does not magically make a protocol safe. It makes one important assumption observable.
Lab: classify parser failures
Use these supplied messages and the Week 5 contract.
A
{"schema_version":1,"motor_temp_c":72,"battery_percent":61,"enabled":true}
B
{"schema_version":1,"motor_temp_c":"72","battery_percent":61,"enabled":true}
C
{"schema_version":1,"motor_temp_c":72,"battery_percent":161,"enabled":true}
D
{schema_version:1,motor_temp_c:72
E
{"schema_version":99,"motor_temp_c":72,"battery_percent":61,"enabled":true}
For each message, classify the earliest observable failure as one of:
- syntax/deserialization;
- schema version;
- type;
- range/semantic validation; or
- accepted by the stated contract.
Then explain what evidence supports the classification.
Error messages should identify the boundary, not leak the system
A useful error might be:
status=rejected field=battery_percent reason=range
A less useful error might dump an entire internal object, environment variables, stack trace, or secret-bearing configuration.
Operational evidence should be enough to diagnose the stage without exposing unrelated sensitive state.
Fail closed does not mean fail chaotically
“Fail closed” is often used to mean a system should deny an operation when authorization cannot be established.
For data parsing, the equivalent idea is usually: do not continue with state you could not interpret according to the contract.
But the system still needs a controlled response:
- reject the message;
- preserve the previous known-good state if that is the documented design;
- record a bounded error;
- alert if the condition matters operationally; and
- avoid partial updates.
Partial updates are a subtle failure mode
Suppose a message contains three fields. The program updates the first two, then rejects the third.
Now runtime state may contain a mixture of old and new values.
For some systems, a better design is:
parse all
↓
validate all
↓
commit state together
This is an atomicity idea: either the accepted update becomes visible as a coherent unit, or it does not.
You will see the same principle later in configuration and change control.
Extend your record
For each supplied message, capture:
Message ID:
Earliest failed stage:
Observed evidence:
Expected receiver action:
What should be logged:
What should not be logged:
Then add one case where parsing succeeds but semantic validation fails.
That distinction is the heart of this lesson.