Lessons Learned Migrating a Large Production Codebase from Zod v3 to v4

The Problem: Inconsistent Email Validation Across the Codebase
A frontend spike revealed that email validation logic between the backend and frontend was not consistent. The backend depended on a large Mailman regex with additional length checks, while the frontend validation was less restrictive.
Why We Chose to Upgrade to Zod v4
As part of improving consistency, maintainability, and centralizing validation logic across the repo, Zod v4 presented a strong opportunity to modernize how validation is handled across the repository.
The goal was to align frontend validation rules with backend requirements while also reducing technical debt. Migrating to standardized Zod-based validation allowed us to:
Prevent email addresses accepted on the frontend but rejected by the backend
Update the email validation regex on the frontend to match backend validation
Maintain consistent validation behavior across the app
Reduce manual regex maintenance and duplication
Centralize email address validation into a reusable shared schema
While the migration was initially motivated by email validation, it quickly became clear that upgrading Zod would provide long-term improvements in developer experience, type safety, and validation reliability.
Unseen Prerequisites That Blocked the Migration
Like many large-scale dependency upgrades, the Zod migration uncovered several prerequisite upgrades that needed to happen first.
When I initially took on on the the project, I discovered our TypeScript version was still on v4.9 while Zod v4 required TypeScript v5.5 or higher. This meant the migration could not happen without first upgrading TypeScript across the repository.
After upgrading TypeScript, additional compatibility issues surfaced with React Hook Form (RHF) and @hookform/resolvers, both required upgrades to support the newer ecosystem.
Adding a bit more complication, our Design System team maintained shared form components that depended on RHF. Their components were running on an older version than what Zod v4 needed to work with, which created a dependency mismatch that prevented a straightforward upgrade.
To resolve this, I collaborated with the Design System team by outlining:
Why upgrading RHF was necessary for Zod v4 compatibility
How mismatch impacted validation and form integrations
The importance of modernizing our ecosystem
Because these shared components were consumed across multiple applications, the upgrade required careful coordination and testing, which extended the migration timeline.
Once the Design System updates were completed and deployed, I was able to successfully install and integrate Zod v4 without additional compatibility issues.
Migration Challenges Inside the Codebase
Once Zod v4 was installed, I quickly realized how loosely validated Zod v3 was compared to v4. Running TypeScript after the upgrade surfaced 80+ type errors across the codebase. While this initially felt overwhelming, a few of the errors highlighted areas where validation logic had been loosely defined or relied on deprecated APIs that included simple updates such as:
Replacing
messagewitherrorReplacing
invalid_type_errorandrequired_errorwitherrorReplacing
import { schema } from “zod”fromimport { z } from “zod”
Zod v4 Runs a Stricter Program
Beyond syntax updates, Zod v4 introduced noticeably stricter type enforcement. In several files, schemas that compiled successfully now failed due to mismatches between expected and actual data shapes. The stricter checks surfaced validation issues such as:
Optional values being treated as required
Inconsistent handling of nullable vs optional fields
Incorrect union definitions
Even found a case where a value was spelled incorrectly but still passed in v3!
Zod v4 also refined how schemas are composed and extended. Several chained validation patterns that worked in v3 required restructuring to align with the new composition behavior.
In particular, complex validation scenarios involving:
.refine().superRefine()Schema merging and extension
transformandcoercechanged to typeunknown
Resolver and Form Integration Updates
Because Zod was tightly integrated with React Hook Form, upgrading Zod required verifying compatibility with the latest resolver versions. Schema changes occasionally affected how form errors were mapped and surfaced in UI components.
In some cases, validation behavior subtly changed due to stricter typing or updated resolver expectations, requiring regression testing across form-heavy workflows.
Key Lesson
Large dependency migrations rarely fail because of the library upgrade itself. They expose inconsistencies, technical debt, and architectural drift that accumulate over time.
Zod v4 did exactly that for our codebase. While the migration required significant effort, it resulted in stronger type guarantees, more consistent validation behavior, and a cleaner foundation for future development.



