Parse, Don't Validate
Turn untrusted input into precise, typed data at the edge of your system, instead of checking it and passing the same loose data along.
Also known as: Parse Don't Validate, Parse, Do Not Validate
Category: Software Development
Tags: software-design, software-development, programming, functional-programming, type-systems, validation, security, error-prevention, principles
Explanation
Alexis King gave this principle its name in a 2019 blog post, "Parse, don't validate". The post is short, and it changes how you think about input for good.
**Validation vs. parsing**:
- **Validation** checks that data is correct, then throws away what it learned. The function returns nothing useful (or a boolean), and the rest of the program still holds the same loosely typed data. Every function downstream has to trust that someone checked it, or check again.
- **Parsing** checks that data is correct AND returns a more precise type that proves it. A function that turns a `string` into an `EmailAddress`, or a `List` into a `NonEmptyList`, keeps what it learned in the type system.
**Why does this matter?**
With validation alone, the knowledge that data is valid lives in your head. The code knows nothing. It's easy to forget a check, to check in the wrong place, or to use data before checking it. King calls the result "shotgun parsing": validation logic scattered across the codebase and mixed with processing. When a check fails halfway through, the system may already have performed half of its side effects. Good luck cleaning that up 😅
Parsing draws a clear line instead. Outside the line, data is untrusted and loosely typed. Inside, data has precise types that guarantee its properties, and the code never needs to re-check.
**How to apply it**:
1. Parse all external input as early as possible: HTTP requests, files, environment variables, user input and yes, LLM output too
2. Return precise types that encode the guarantees (non-empty, positive, well-formed, within range)
3. Fail at the boundary, before any processing or side effect happens
4. Make the rest of the code depend on the precise types, never on raw data
5. Use a schema library (Zod, Pydantic, Serde, io-ts) to keep parsing declarative
**An example**:
Instead of `validateConfig(raw: unknown): void` followed by unsafe casts, write `parseConfig(raw: unknown): Config`. It either returns a fully typed `Config` or fails. Every function downstream accepts a `Config`, so you can't pass unchecked data by accident anymore.
**How it connects**:
Parse, Don't Validate is the boundary-side companion of Make Illegal States Unrepresentable. Precise types define what valid data looks like. Parsing is how untrusted data becomes those types. Together, they build a Pit of Success where the code inside your system only ever sees valid data.
Related Concepts
← Back to all concepts