Denshin / Blog / Engineering
Why Zod replaced our hand-rolled validators
We resisted Zod for a while. The migration took a sprint and we never looked back. Here's what changed, what it cost, and why the inferred types matter more than the schemas.
Denshin Engineering · Engineering Team · 1 March 2026 · 1 min read
For a long time, we hand-rolled validation. Zod kept showing up in articles and we kept ignoring it. The handwave was: "we already have a few helpers, why pull in a dependency?"
The migration happened in a single sprint, by accident. We were adding a new endpoint that had to validate a moderately complex nested payload. The handwritten validator for it was getting longer than the actual handler. One of the engineers said, "give me a day with Zod and we'll see". A day later, the validator was eight lines, the type was inferred, and we started questioning every other handwritten check on the codebase.
The thing nobody tells you in the marketing posts: the real win isn't the schemas, it's the inferred types. We were maintaining two parallel definitions for every payload (a TypeScript interface for the editor, and a JavaScript validator for runtime), and they drifted. Zod collapses both into one source of truth that the compiler agrees with.
What it cost us:
- About 15 KB on the client bundle. Not nothing, not a deal-breaker.
- One week of "translating" old validators into Zod schemas, mostly mechanical, no logic changes.
- A bit of getting used to the syntax for unions and discriminated unions. Worth the friction.
What we got back: roughly 200 fewer lines of validation code per project, type errors that catch real bugs at compile time, and a single place to look when somebody asks "what does this endpoint accept".
The other thing that surprised us: forms got better. We pair Zod schemas with react-hook-form's zodResolver and the same schema validates the form on the client and the request on the server. The error messages match. The fields that need attention get the right outline. Nobody has to maintain two regexes for the same email field.
If you're still hand-rolling, you're paying a hidden tax. Try it for a single endpoint. It's hard to go back.
Tags: TypeScript, Zod, Validation, Refactor
All posts · Work with Denshin