Parse, Don't Validate (2019)

“Parse, don’t validate” is a programming approach that urges developers to turn raw, untrusted input into rich, well-typed domain objects as early as possible, so that invalid states become unrepresentable in the rest of the code. Commenters explore how this pattern improves correctness and readability across languages from F#, Rust and TypeScript to C# and Go, contrasting it with ad-hoc checks, implicit “sane defaults,” and repeated validation scattered through business logic. They also highlight trade-offs, such as when strict failure is preferable to fallback behavior and how far type systems can realistically go in encoding invariants like “non-empty” or “verified” data.

Overall reception & scope of the idea

  • Many commenters call the article one of their favorites and “seminal,” returning to it regularly.
  • Core takeaway: it is not “don’t validate,” but “validate once, at the boundary, and turn raw data into richer types so the rest of the code can assume invariants.”
  • Some note the title causes confusion, leading people to argue against positions the article doesn’t actually take.

Error handling vs “sane defaults”

  • Debate around patterns like TryParse with silent fallbacks to “sane defaults.”
  • Several argue this is dangerous: invalid input should fail loudly, not be silently replaced, or bugs become hard to trace.
  • Others counter that strict failure is sometimes user-hostile (e.g., malformed footnote, hotel room selection); partial success with warnings can be preferable, but must not be silent.
  • Crowdstrike’s outage is cited as an example of not planning for invalid configs at all.

Using types to make invalid states impossible

  • Strong support for modeling states in the type system: e.g., UncheckedEmailValidEmailVerifiedEmail, or User vs VerifiedUser.
  • This avoids boolean flags and re-checks, and lets the compiler enforce correct usage.
  • Disagreement on how far to go: some prefer only “valid” types; others find intermediate-state types essential, especially for multi-step workflows.
  • Non-empty lists and smart constructors are discussed as practical patterns.

Language-specific perspectives

  • Pattern seen as working well in F#, C#, Rust, TypeScript (with type predicates, Zod, effect/schema), and in FP languages with algebraic data types.
  • Go’s zero values and JSON handling make this style harder, forcing more validation between parse and use.
  • Haskell/OCaml list types and non-empty variants are dissected; historical choices are called “warts” but entrenched.
  • Clarifications around Void/void vs unit types to avoid confusion between “no value” and “never returns.”

Validation placement, duplication, and security

  • Advocates stress that once parsed into safe types, repeated checks (“shotgun parsing”) are a code smell and violate DRY.
  • Others argue re-checking can be justified as defense in depth but may reflect “anxiety-driven development.”
  • One concern: exposed APIs still need protection against malicious payloads and resource exhaustion; reply is that buggy parsers are just bugs, and centralizing parsing tends to improve, not reduce, robustness.