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.