Skip to content

F4. Parse, Don't Validate

Diagnoses: D4. Scattered Validation and Boolean Blindness Related fixes:

  • F3 (Functional Core, Imperative Shell) — the shell does the parsing; the core only sees parsed types
  • F10 (Single Point of Knowledge) — the parser is the single authoritative definition of the data’s valid shape
  • F11 (Lifted Invariants) — same establish-once principle, applied to invariants that emerge between internal pipeline stages after data is already typed; F4 is the boundary case

(Alexis King, 2019)

The key insight: validation checks that data is well-formed but throws away the evidence. A function that returns boolean tells the caller “yes” or “no” but doesn’t encode what was verified into the type system — this is boolean blindness. Downstream code is left to trust (or re-check) on faith.

Parsing is the antidote. Parsing converts data into a richer type where invalid states are unrepresentable. After parsing, downstream code can’t encounter surprises — the type system (or the structure of the data) guarantees it.

This aligns with the Functional Core / Imperative Shell: the shell does the parsing (converting raw external data into typed internal data), and the core only ever sees the parsed, well-typed representation.

For every system boundary (HTTP request handler, file reader, database query result, user event):

  1. Define an internal type that represents the valid shape of the data. Invalid states should be unrepresentable in this type.
  2. Write a parser at the boundary that converts raw input into this type, failing explicitly if the input is malformed.
  3. Trust the type downstream. Once data has been parsed, no further validation should be necessary. If you find yourself checking for null or invalid values deep in the interior, the boundary parser is incomplete.

The boundary is where the messy world meets your clean domain. Everything inside the boundary speaks the language of your domain types.