We stuck with Yup and ignored Joi, Zod and god knows what else has come down the pipeline since. Rather than wasting time upgrading, we've instead built useful features.
That said, we are slowly phasing out our React frontend for one of our apps page by page and replacing it with what we use over the rest of apps: Phoenix + Liveview. The changeset system for validations has proven itself time and time again to both be fast but also to handle many of the curly validation problems we might have. It also has barely changed since its release.
If you have a disciplined frontend team then you might be able to make it work. They have to be able to ignore the latest shiny thing that has come along, but if you don't then you'll have a team that is busy constantly upgrading to the latest shiny library rather than getting anything done.
> "a team that is busy constantly upgrading to the latest shiny library rather than getting anything done"
Your disdain for resume-driven development is fair enough, but it's not like there's a binary choice between Phoenix/LiveView and spinning your wheels chasing the new shiny.
I love using Elixir on the backend, and I tried really, really hard to get LiveView, but it was just too hard of a paradigm shift for me to manage more complex state, plus too hard to walk away from my productivity with React. For example, something as simple as having inline edit on a list of widgets is super easy in React, but I found it _very_ challenging to do with LiveView, as you try and implement more features such as only editing one item at a time, etc.
I've actually found a bit of a resurgence in productivity with React on the front-end and Elixir/Absinthe/Ash Framework on the backend. With GraphQL I get fully typed queries, and Ash Framework on the Elixir side helps a ton with removing the boiler plate of code required for GraphQL, since Ash (mostly) generates the schema for me. A bit of a learning curve with Ash, but now that it's clicked, I feel more productive than I did with just Ecto/Contexts/LiveView.
https://www.ash-hq.org - there's a recent PragProg book on it that really helped it click for me.
I've used LiveView a little in the past, and besides the lack of static typing (at the time, I think Elixir has gotten some type annotations since), I really liked it and found it easy to work with.
Ecto though, I could never figure out how to use. If I could just make SQL queries in an ORM-style I would understand it, but the repositories and auto generated relations and such I couldn't figure out. Do you have any good resources for learning ecto? I didn't find the official docs helpful.
I only use Zod in a single library, so my experience with it is pretty minimal. One thing I have really enjoyed with it is the low maintenance.
So, when I saw this post and read the first few paragraphs, I was filled with dread for having to do yet another major dependency update. Reading of all the improvements- surly the ‘breaking changes’ list must be massive.
Having read the full article, it seems like the breaking changes have been kept within reason. One major improvement that stands out to me is the improved Recursive types support, but there is certainly any number of improvements for others to be excited about.
The thing I enjoyed most is that excitement of the Arthur really shines throughout the whole article. I’m so thankful this library exists and is being lovingly maintained by someone who is genuinely interested in it.
Even if the breaking changes were massive, you could keep the dependency pinned at the version you're using and not upgrade if the new features aren't useful/compelling
I use it in a fairly big project. The only breaking change for me was z.record() now requiring two parameters, previously the second parameter was optional. The rest are deprecations that can be easily fixed like .email, etc.
Zod is installed in nearly every project I use. It’s an essential part of my toolkit. I adore this library. It near perfect as-is and these additions make it even better. Thanks for all the hard work.
I used to use Zod until I realised it’s rather big (for many projects this isn’t an issue at all, but for some it is). Now I use Valibot which is basically the same thing but nice and small. I do slightly prefer Zod’s API and documentation, though.
Edit to add: aha, now I read further in the announcement, looks like @zod/mini may catch up with valibot -- it uses the same function-based design at least, so unused code can be stripped out.
Though exciting, looks like there's still room for shrinkage, the linked article puts a bare-minimum gzipped @zod/mini at 1.88kb, while Valibot is at 0.71kb [1].
My team has been building a greenfield SaaS app for the past ~9 months and when we started, we went all in on Zod and so far the experience has been pretty fantastic. Zod is very intuitive to use and being able to generate typescript types off the schemas is very useful. Though with that said if you're already using Yup or Joi I don't recommend switching as there's just not enough of a reason to switch over.
Surprised not to see more people ask about performance profile of v4.
Zod is great in terms of API, but a no-go in terms of performance.
We ended up writing babel plugins (https://github.com/gajus/babel-plugin-zod/) and using it along with zod-accelerator, which improves performance, but breaks in various edge-cases.
I completely understand TypeScript, Zod not so much. The context here is performance.
My understanding is that Zod performs synchronous operations to validate the schemas. Something about "using the tool correctly" resonates here. Maybe don’t validate very large and nested schemas frequently and things like that.
But I can’t help but think it is adding another layer of potential footguns that you have to be mindful about. Maybe the benefits outshine the risks in disciplined teams that understand both the tool and the runtime. However I can’t help but think about the less disciplined teams abusing Zod and then wondering why things are slow.
Maybe I'm misunderstanding some aspect here, but it seems to me there are two choices for handling external data in TypeScript:
Either you parse / validate incoming data at an API boundary with a tool like zod, or you do not, and just trust that the data matches the exact shape you expect, opening yourself up to completely unexpected errors / behaviors if it does not.
The latter is probably a decent option if you fully control all components back to front, but if you really value type safety, the former seems like the way to go?
> I completely understand TypeScript, Zod not so much
Different tools. I only use TS but once you add external data to the mix you cannot escape `any` and `unknown` — so what you do is use `as`. Congrats, your types are now useless.
Zod would close that gap as it enforces types on external resources (e.g. `fetch` or `readFile`)
You can write type assertion functions that validate the input is a given shape.
In theory, you'd use these type assertion methods at any API boundaries a single time for external inputs/outputs in a "Parse, Don't Validate" approach to cover your bases.
I've created some crazy types with zod since v3 released, these look like really interesting changes for v4.
I've been bit by performance issues, and even a few issues where I just couldn't infer types, with some of the more complex schemas I tried to define, especially with multiple .extend() and .omit() calls.
The zod-mini library looks particularly interesting. I don't mind the more functional approach with nested functions rather than chaining. Reminds me of ramda, that library is pretty massive but still easy to tree shake since everything is functional.
This is awesome, bundle size has been my number one issue preventing me from trying out Zod as v3 shipped with validations that were unnecessary for frontend use but weren't able to be treeshaken.
I do wonder if it might have fixed the type complexity issue that has prevented me from exporting Zod schemas from a library, but I suppose I could try finding this out later
Definitely a little more ergonomic than type box, imo, but at the end of the day they’re very similar. I use typebox mostly because there is terrible zod support for Fastify. Both are great libs!
v3 didn't last four years. Can we expect much different from v4?
If you're migrating your zod 3 code, I'd migrate to something more stable and long-lived than v4 is likely to be -- unless you want to be going through it all again in a few years (and yet again a few years after that, etc).
I don't blame the maintainers (I assume they don't have a fat support and maintenance contract)
But developers need to go in with their eyes open when it comes to adopting these throw-away libraries and frameworks.
This seems unnecessarily cynical, 4 years is a pretty long time to go between major versions for a library like this, and if you look at the breaking changes they're not exactly severe. This isn't a React Router situation where you need to re-write your whole app.
> I don't blame the maintainers (I assume they don't have a fat support and maintenance contract)
So what you're saying is that people need to be aware of software being an iterative process and that maintainers of libraries can't know in advance all the things that might come up in 4 years time?
I don't know of any software that doesn't have these problems. Either you snapshot a thing and never do updates or you continually update it and have things change.
The update causes almost 0 breaking changes. I don't think this classifies as a "throwaway" library.
> But developers need to go in with their eyes open when it comes to adopting these throw-away libraries and frameworks.
For contrast: My entire company is less than 4 years old and any code from 6 months ago feels old because things (in a business requirements sense) change so fast. Zod lasting 3 years is fine.
Although I do wish v4 came a little sooner because we just started using Zod a few months ago.
Zod has only been around as 1.0 since 2020, so it's probably not a project with enough history you'd have been interested in for many more years yet anyways.
Of course the bigger question here for most is how large are the breaking changes, not if there is a decade between them.
This is a little disingenuous. As far as I know, v3 isn't going anywhere. There's what... weeks until May 2025, which would be four years?
4 years in JavaScript land is actually pretty long. Zod has a pretty good maintenance record. I don't see how a statement like yours can be made without snark. Calling it a "throw-away" library is pretty brash.
This looks like a good update that sticks to the formula.
> 4 years in JavaScript land is actually pretty long
For non-JS developers to get a sense of how long this is, companies have probably migrated from React to Vue to Svelte to Solid and then back to React in this time.
That said, we are slowly phasing out our React frontend for one of our apps page by page and replacing it with what we use over the rest of apps: Phoenix + Liveview. The changeset system for validations has proven itself time and time again to both be fast but also to handle many of the curly validation problems we might have. It also has barely changed since its release.
If you have a disciplined frontend team then you might be able to make it work. They have to be able to ignore the latest shiny thing that has come along, but if you don't then you'll have a team that is busy constantly upgrading to the latest shiny library rather than getting anything done.
Your disdain for resume-driven development is fair enough, but it's not like there's a binary choice between Phoenix/LiveView and spinning your wheels chasing the new shiny.
A lot of that is going away with better LSP support and better LLM suggestions.
But elixir and LiveView are generally great (I LOVE ecto), but building component frameworks and component communication still feels a bit janky.
I've actually found a bit of a resurgence in productivity with React on the front-end and Elixir/Absinthe/Ash Framework on the backend. With GraphQL I get fully typed queries, and Ash Framework on the Elixir side helps a ton with removing the boiler plate of code required for GraphQL, since Ash (mostly) generates the schema for me. A bit of a learning curve with Ash, but now that it's clicked, I feel more productive than I did with just Ecto/Contexts/LiveView.
https://www.ash-hq.org - there's a recent PragProg book on it that really helped it click for me.
Ecto though, I could never figure out how to use. If I could just make SQL queries in an ORM-style I would understand it, but the repositories and auto generated relations and such I couldn't figure out. Do you have any good resources for learning ecto? I didn't find the official docs helpful.
So, when I saw this post and read the first few paragraphs, I was filled with dread for having to do yet another major dependency update. Reading of all the improvements- surly the ‘breaking changes’ list must be massive.
Having read the full article, it seems like the breaking changes have been kept within reason. One major improvement that stands out to me is the improved Recursive types support, but there is certainly any number of improvements for others to be excited about.
The thing I enjoyed most is that excitement of the Arthur really shines throughout the whole article. I’m so thankful this library exists and is being lovingly maintained by someone who is genuinely interested in it.
Edit to add: aha, now I read further in the announcement, looks like @zod/mini may catch up with valibot -- it uses the same function-based design at least, so unused code can be stripped out.
[1] https://github.com/anatoo/zod-vs-valibot
Zod is great in terms of API, but a no-go in terms of performance.
We ended up writing babel plugins (https://github.com/gajus/babel-plugin-zod/) and using it along with zod-accelerator, which improves performance, but breaks in various edge-cases.
My understanding is that Zod performs synchronous operations to validate the schemas. Something about "using the tool correctly" resonates here. Maybe don’t validate very large and nested schemas frequently and things like that.
But I can’t help but think it is adding another layer of potential footguns that you have to be mindful about. Maybe the benefits outshine the risks in disciplined teams that understand both the tool and the runtime. However I can’t help but think about the less disciplined teams abusing Zod and then wondering why things are slow.
Either you parse / validate incoming data at an API boundary with a tool like zod, or you do not, and just trust that the data matches the exact shape you expect, opening yourself up to completely unexpected errors / behaviors if it does not.
The latter is probably a decent option if you fully control all components back to front, but if you really value type safety, the former seems like the way to go?
Different tools. I only use TS but once you add external data to the mix you cannot escape `any` and `unknown` — so what you do is use `as`. Congrats, your types are now useless.
Zod would close that gap as it enforces types on external resources (e.g. `fetch` or `readFile`)
You can write type assertion functions that validate the input is a given shape.
In theory, you'd use these type assertion methods at any API boundaries a single time for external inputs/outputs in a "Parse, Don't Validate" approach to cover your bases.
I've been bit by performance issues, and even a few issues where I just couldn't infer types, with some of the more complex schemas I tried to define, especially with multiple .extend() and .omit() calls.
The zod-mini library looks particularly interesting. I don't mind the more functional approach with nested functions rather than chaining. Reminds me of ramda, that library is pretty massive but still easy to tree shake since everything is functional.
> Zod 4 introduces first-party JSON Schema conversion via z.toJSONSchema().
v3 didn't last four years. Can we expect much different from v4?
If you're migrating your zod 3 code, I'd migrate to something more stable and long-lived than v4 is likely to be -- unless you want to be going through it all again in a few years (and yet again a few years after that, etc).
I don't blame the maintainers (I assume they don't have a fat support and maintenance contract)
But developers need to go in with their eyes open when it comes to adopting these throw-away libraries and frameworks.
> I don't blame the maintainers (I assume they don't have a fat support and maintenance contract)
What's the implication here?
In fact, zod is one of the leading forces behind this: https://github.com/standard-schema/standard-schema
A major version increase in four years doesn’t exactly scream “throw-away” to me.
I don't know of any software that doesn't have these problems. Either you snapshot a thing and never do updates or you continually update it and have things change.
The update causes almost 0 breaking changes. I don't think this classifies as a "throwaway" library.
For contrast: My entire company is less than 4 years old and any code from 6 months ago feels old because things (in a business requirements sense) change so fast. Zod lasting 3 years is fine.
Although I do wish v4 came a little sooner because we just started using Zod a few months ago.
Of course the bigger question here for most is how large are the breaking changes, not if there is a decade between them.
4 years in JavaScript land is actually pretty long. Zod has a pretty good maintenance record. I don't see how a statement like yours can be made without snark. Calling it a "throw-away" library is pretty brash.
This looks like a good update that sticks to the formula.
For non-JS developers to get a sense of how long this is, companies have probably migrated from React to Vue to Svelte to Solid and then back to React in this time.