1 điểm bởi GN⁺ 2025-05-20 | 1 bình luận | Chia sẻ qua WhatsApp
  • Zod 4 được phát hành ở phiên bản ổn định sau 1 năm phát triển, khắc phục các giới hạn thiết kế của Zod 3 và cải thiện mạnh hiệu năng, kích thước bundle và hiệu quả biên dịch TypeScript
  • Zod 3 ra mắt vào tháng 5/2021 với 2.700 GitHub stars và 600.000 lượt tải mỗi tuần, nay đã tăng lên 37.8k stars31 triệu lượt tải/tuần
  • Trong benchmark, phân tích chuỗi nhanh hơn 14 lần, phân tích mảng nhanh hơn 7 lần, phân tích object nhanh hơn 6.5 lần; số lần khởi tạo kiểu của tsc trong ví dụ đơn giản cũng giảm từ hơn 25.000 xuống khoảng 175
  • Kích thước bundle lõi theo gzip giảm từ 12.47kb của Zod 3 xuống 5.36kb ở bản thường của Zod 4, còn Zod Mini với API hàm có thể tree-shaking chỉ còn 1.88kb
  • Các tính năng mới gồm chuyển đổi JSON Schema, metadata registry, suy luận kiểu object đệ quy, quốc tế hóa, pretty-print lỗi, kiểu template literal, z.stringbool(), tham số error hợp nhất, và z.discriminatedUnion() được cải thiện

Vì sao Zod 4 trở thành major version mới

  • Zod 4 đã trở thành bản ổn định sau 1 năm phát triển tích cực, mang đến nền tảng nhanh hơn, nhỏ hơn và hiệu quả hơn với tsc, đồng thời hiện thực hóa các tính năng được yêu cầu từ lâu
  • Zod v3.0 phát hành vào tháng 5/2021, khi đó có 2.700 GitHub stars và 600.000 lượt tải mỗi tuần
  • Hiện tại Zod ghi nhận 37.8k GitHub stars và 31 triệu lượt tải mỗi tuần
    • 6 tuần trước khi bản beta ra mắt, số lượt tải mỗi tuần là 23 triệu
  • Sau 24 phiên bản minor, codebase của Zod 3 đã chạm tới giới hạn, và các tính năng/cải tiến được yêu cầu nhiều nhất đều cần thay đổi phá vỡ tương thích
  • Zod 4 đã đóng 9 trong 10 issue công khai được upvote nhiều nhất

Hiệu năng parsing và hiệu quả biên dịch TypeScript

  • Zod 4 cung cấp các benchmark có thể chạy trực tiếp từ repository
  • Cải thiện hiệu năng parsing:
    • Parsing chuỗi: nhanh hơn 14 lần
    • Parsing mảng: nhanh hơn 7 lần
    • Parsing object: nhanh hơn 6.5 lần
  • Benchmark parsing object chạy Moltar validation library benchmark
  • Theo tsc --extendedDiagnostics, số lần khởi tạo kiểu giảm mạnh khi biên dịch một file đơn giản
    • Dùng "zod/v3": hơn 25.000 lần
    • Dùng "zod/v4": khoảng 175 lần
  • Zod 4 thiết kế lại và đơn giản hóa generic của ZodObject cùng các lớp schema khác để tránh bùng nổ số lần khởi tạo
  • Mẫu lặp nối .extend().omit() trong Zod 3 mất 4000ms để biên dịch, và thêm các lệnh gọi .extend() có thể gây lỗi "Possibly infinite"
  • Cùng mẫu đó trong Zod 4 biên dịch trong 400ms, nhanh hơn 10 lần
  • Trong tương lai, khi dùng cùng compiler tsgo, hiệu năng editor của Zod 4 có thể mở rộng tốt hơn tới các schema và codebase lớn hơn

Kích thước bundle và Zod Mini

  • Kích thước bundle lõi được so sánh bằng cách bundle một script xác thực z.boolean() đơn giản bằng rollup
  • Kích thước bundle lõi theo gzip:
    • Zod 3: 12.47kb
    • Zod 4 bản thường: 5.36kb
  • Bundle lõi của Zod 4 bản thường nhỏ hơn khoảng 57% so với Zod 3, tức giảm 2.3 lần
  • API thiên về method của Zod vốn khó tree-shaking từ gốc, nên ngay cả script z.boolean() đơn giản cũng kéo theo cả phần cài đặt của các method không dùng như .optional().array()
  • Zod Mini cung cấp API hàm có thể tree-shaking, tương ứng 1:1 với zod
    • Khi Zod dùng method, Zod Mini thường dùng các wrapper function
    • Các method parsing giống nhau giữa Zod và Zod Mini
    • Dùng method .check() tổng quát để thêm refinement
  • Bản Zod thường vẫn tiếp tục được khuyến nghị cho hầu hết trường hợp sử dụng; các dự án có ràng buộc kích thước bundle cực kỳ nghiêm ngặt có thể cân nhắc Zod Mini
  • Khi dùng zod/mini, kích thước bundle gzip là 1.88kb
    • Giảm 85% so với Zod 3, tức nhỏ hơn 6.6 lần
    • Bảng so sánh: Zod 3 12.47kb, Zod 4 thường 5.36kb, Zod 4 Mini 1.88kb
  • Xem thêm trong tài liệu zod/mini

Metadata và JSON Schema

  • Zod 4 giới thiệu một hệ thống mới để thêm metadata strongly-typed vào schema
  • Metadata không được lưu trong chính schema, mà được lưu trong schema registry liên kết schema với typed metadata
  • Có thể tạo registry bằng z.registry(), và cũng có thể đăng ký tiện lợi qua method .register() của schema
  • Zod export z.globalRegistry, một global registry nhận metadata tương thích JSON Schema phổ biến
  • Method .meta() giúp thêm schema vào z.globalRegistry một cách thuận tiện
  • Để tương thích với Zod 3, .describe() vẫn được giữ lại, nhưng trong Zod 4, .meta() được khuyến nghị hơn
  • Zod 4 cung cấp chuyển đổi JSON Schema first-party thông qua z.toJSONSchema()
  • Metadata trong z.globalRegistry được tự động đưa vào đầu ra JSON Schema
  • Thông tin tùy biến cho JSON Schema được tạo ra có trong JSON Schema docs

Biểu diễn schema và tính năng kiểu mới

  • Zod 4 có thể suy luận đúng kiểu object đệ quy
    • Có thể biểu diễn kiểu đệ quy và kiểu đệ quy lẫn nhau
    • Khác với mẫu kiểu đệ quy ở Zod 3, không cần ép kiểu
    • Schema kết quả là một instance ZodObject thông thường và có thể dùng đầy đủ method
  • Đã thêm File schema để xác thực instance File
  • z.templateLiteral() biểu diễn kiểu template literal của TypeScript
    • Các kiểu schema Zod có thể chuyển thành chuỗi sẽ lưu regex nội bộ
    • Đối tượng hỗ trợ gồm chuỗi, định dạng chuỗi như z.email(), số, boolean, bigint, enum, literal, undefined/optional, null/nullable, và template literal khác
    • Constructor z.templateLiteral nối chúng lại thành một super-regex duy nhất
    • Các định dạng chuỗi như z.email() được cưỡng chế đúng, nhưng custom refinement thì không
    • Chi tiết xem tại template literal docs
  • Đã thêm các định dạng số mới để biểu diễn integer độ rộng cố định và kiểu số dấu phẩy động
    • Trả về instance ZodNumber đã được gắn sẵn các ràng buộc inclusive minimum/maximum phù hợp
  • Cũng thêm các định dạng số bigint vượt ngoài phạm vi có thể biểu diễn an toàn bằng number của JavaScript
    • Trả về instance ZodBigInt đã được gắn sẵn các ràng buộc inclusive minimum/maximum phù hợp
  • z.literal() nay có thể nhận tùy chọn nhiều giá trị

Lỗi, quốc tế hóa và định dạng chuỗi

  • Zod 4 giới thiệu API locales mới để dịch toàn cục thông báo lỗi sang nhiều ngôn ngữ
  • Danh sách đầy đủ các locale được hỗ trợ có tại Customizing errors và sẽ được cập nhật khi có thêm ngôn ngữ
  • Zod triển khai hàm cấp cao z.prettifyError để chuyển ZodError thành chuỗi ở định dạng thân thiện với người dùng
    • Định dạng hiện chưa thể cấu hình
    • Có thể sẽ thay đổi trong tương lai
    • Nếu đang dùng zod-validation-error, bạn vẫn có thể tiếp tục dùng
  • Mọi định dạng chuỗi như email đều được nâng lên thành các hàm cấp cao trong module z
    • Ngắn gọn hơn và thuận lợi hơn cho tree-shaking
    • API method tương ứng như z.string().email() vẫn dùng được nhưng đã ở trạng thái deprecated
    • Dự kiến sẽ bị loại bỏ ở major version tiếp theo
  • API z.email() hỗ trợ custom regular expression
    • Không có một canonical email regex duy nhất; mỗi ứng dụng có thể chọn tiêu chuẩn chặt hơn hoặc lỏng hơn
    • Zod export sẵn một vài regex phổ biến để tiện sử dụng

Chuyển đổi boolean và đơn giản hóa tùy biến lỗi

  • z.coerce.boolean() trước đây là API đơn giản chuyển các giá trị falsy thành false, truthy thành true
    • false, undefined, null, 0, "", NaN... sẽ thành false
    • Hành vi này nhất quán với các API z.coerce khác
  • Zod 4 giới thiệu z.stringbool() để coercion boolean tinh vi hơn theo kiểu env
    • Có thể tùy biến các giá trị truthy và falsy
    • Xem chi tiết tại tài liệu z.stringbool() docs
  • Phần lớn thay đổi phá vỡ tương thích trong Zod 4 liên quan tới API tùy biến lỗi
  • Việc tùy biến lỗi được hợp nhất vào một tham số error duy nhất
    • message được thay bằng error
    • Tham số message vẫn được hỗ trợ nhưng đã deprecated
    • invalid_type_errorrequired_error được thay bằng error theo cú pháp hàm
    • errorMap cũng được thay bằng error theo cú pháp hàm

Tính kết hợp, refinement và transform

  • z.discriminatedUnion() nay hỗ trợ nhiều kiểu schema trước đây chưa được hỗ trợ
    • Bao gồm union và pipe
    • Có thể dùng một discriminated union làm member của discriminated union khác, giúp có thể kết hợp
  • Trong Zod 3, refinement được lưu trong lớp ZodEffects bao quanh schema gốc
    • Cấu trúc này khiến việc trộn .refine() với các method schema khác như .min() trở nên bất tiện
  • Trong Zod 4, refinement được lưu bên trong schema, cho phép dùng .refine() cùng các method schema khác một cách tự nhiên
  • Method .overwrite() mới biểu diễn các transform không làm thay đổi inferred type
    • .transform() có kiểu đầu ra không thể introspect ở runtime, và hàm transform có thể trả về bất cứ thứ gì như một black box
    • Vì vậy không có cách an toàn để chuyển đổi sang JSON Schema
    • .overwrite() trả về instance của lớp gốc
    • Hàm overwrite được lưu như refinement và không sửa inferred type
  • Các method .trim(), .toLowerCase(), .toUpperCase() hiện được triển khai lại bằng .overwrite()

Nền tảng cho tác giả thư viện

  • Việc bổ sung Zod Mini dẫn tới sự ra đời của subpackage zod/v4/core, chứa các chức năng lõi dùng chung giữa Zod và Zod Mini
  • zod/v4/core mở rộng Zod từ một thư viện đơn lẻ thành nền tảng xác thực nhanh có thể nhúng vào thư viện khác
  • Nếu đang xây dựng thư viện schema, có thể tham khảo cách triển khai của Zod và Zod Mini để xây trên zod/v4/core
  • Zod cung cấp hướng dẫn For library authors
    • Trình bày best practice khi xây dựng trên Zod
    • Trả lời các câu hỏi phổ biến về cách hỗ trợ đồng thời Zod 3, Zod 4 và Mini

1 bình luận

 
GN⁺ 2025-05-20
Các ý kiến trên Hacker News
  • Tôi là tác giả, cứ hỏi tôi bất cứ điều gì. Về chính sách phiên bản, tôi đã ghi lại khá chi tiết lý do của cách tiếp cận này tại đây: https://github.com/colinhacks/zod/issues/4371
    Rốt cuộc npm không được thiết kế để xử lý tốt tình huống mà Zod đang gặp phải. Hàng chục đến hàng trăm thư viện import trực tiếp interface/class của Zod rồi dùng chúng trong API công khai của chính họ, nên mỗi lần Zod tăng phiên bản major thì các thư viện đó cũng phải phát hành phiên bản major mới
    Xét riêng lẻ thì điều này hợp lý, nhưng với Zod nó có thể tạo ra một trận lở tuyết phiên bản gây đau khổ cho tất cả mọi người, và có vẻ như phần lớn hệ sinh thái sẽ bị ghim mãi ở v3
    Vì vậy chúng tôi chọn cách tương tự Go: thay vì package phát hành một phiên bản mới phá vỡ tương thích, nó thêm một subpath mới. Trong hệ sinh thái TypeScript, thư viện có thể chỉ đặt một peer dependency là zod@^3.25.0, rồi import những thứ cần thiết từ "zod/v3""zod/v4" để hỗ trợ đồng thời cả hai phiên bản

    • Việc cải thiện hiệu năng tsc trong các codebase lớn đặc biệt đáng hoan nghênh, và thay đổi về discriminated union cũng có vẻ sẽ giúp ích rất nhiều cho một số kịch bản vốn còn thiếu sót từ trước đến nay
      Tuy hiểu lý do phải chọn một chính sách phiên bản hơi lạ vì hoàn cảnh đặc thù, nhưng từ góc nhìn của một đội không cần lo xung đột phiên bản Zod trong dependency bắc cầu, tôi vẫn muốn có cả package 4.0.0
      Nếu tôi hiểu đúng thì phải đổi import sang "zod/v4", đây là một thay đổi rất ồn ào, và có thể khá phiền vì phải dùng rule lint để bắt vấn đề IDE tự động import từ 'zod'
    • Cách này rất hợp lý. Nó cũng có nghĩa là các bản cập nhật bảo mật cho phiên bản cũ có thể được cung cấp cùng trong một bản phát hành của cùng codebase
    • Tôi đang dùng di động nên không rõ bài viết đã đề cập chưa, nhưng tôi thắc mắc việc sửa .optional() trong TypeScript có nằm trong nhóm 9–10 issue hàng đầu được giải quyết không: https://github.com/colinhacks/zod/issues/635
      Đây là nỗi đau lớn nhất của tôi với Zod, nhưng Zod vẫn quá tốt nên tôi chấp nhận và tiếp tục dùng
    • Nhờ các tính năng mới, có vẻ tôi sẽ loại bỏ được nhiều đoạn hack tạm ở local. Tôi đang dùng một phiên bản tự làm của https://github.com/raflymln/zod-key-parser để tránh gõ sai tên form, nên khá ngạc nhiên là tính năng như vậy chưa được đưa trực tiếp vào thư viện
      Tôi thắc mắc liệu bạn xem đây là ngoài phạm vi của Zod, hay đơn giản là chưa có thời gian triển khai. Thảo luận liên quan nằm ở https://github.com/colinhacks/zod/discussions/2134
    • Nếu ý là phiên bản sớm nhất của Zod 4 là 3.25.0 thì nghe có vẻ hơi vô lý
      Tôi đã dùng bản alpha của Zod trong vài tháng và chỉ muốn sửa package.json để nâng cấp, nhưng giờ có vẻ tôi phải lục lại toàn bộ lịch sử git
      Tôi rất biết ơn dự án này, nhưng riêng phản hồi này thì có vẻ các bạn đã làm nó khó hơn mức cần thiết. Tôi nghĩ có lẽ có thể phát hành cả 4.x cùng với 3.x
  • Nội dung “để đơn giản hóa việc migration, Zod 4 được phân phối cùng Zod 3 như một phần của bản phát hành zod@3.25, và được import từ subpath "/v4"” trông như dấu hiệu cho thấy quản lý dependency của npm đang hoàn toàn rối tung
    Peer dependency hỏng đến mức v4 phải giả vờ là v3

    • Tôi là tác giả. Lý do của cách tiếp cận này được tôi viết khá chi tiết ở đây: https://github.com/colinhacks/zod/issues/4371
      Đúng là npm không quản lý tốt tình huống mà Zod đang gặp. Tuy nhiên Zod bị ràng buộc bởi việc gần như không nhận thư viện nào. Hàng chục đến hàng trăm thư viện import trực tiếp interface/class từ "zod" và dùng chúng trong API công khai của mình
      Những thư viện như vậy phải phát hành phiên bản major mới mỗi khi Zod tăng major, và với Zod điều đó có thể gây ra trận lở tuyết phiên bản khiến mọi người đều khổ sở. Nói thật, tôi đã nghĩ rằng phần lớn hệ sinh thái sẽ bị kẹt mãi ở v3
      Vì vậy, tương tự Go, chúng tôi chọn cách thêm subpath mới thay vì phát hành phiên bản package mới cho mỗi lần phát hành phá vỡ tương thích. Trong hệ sinh thái TypeScript, chỉ với một peer dependency zod@^3.25.0, có thể hỗ trợ đồng thời "zod/v3""zod/v4"
    • Tôi không nghĩ kết luận “peer dependency bị hỏng nên v4 giả vờ là v3” là đúng. Theo tôi, Zod v4 được đưa vào trong v3 là để người dùng chuyển dần
      Tức là refactor từng chỗ sử dụng sang import ... from 'zod/v4', rồi khi xong thì nâng hẳn lên v4
    • Có cảm giác cứ nói tiêu cực là được upvote. Quyết định này gần với một cách thực tế để cho phép các thư viện có nhiều thay đổi phá vỡ tương thích được thay đổi dần dần, hơn là một giới hạn bản chất của chính npm
    • Có thể vì dùng mỗi npm suốt nhiều năm nên góc nhìn của tôi bị hẹp, nhưng tôi tò mò có cách nào tốt hơn để migration lặp đi lặp lại từ v3 sang v4, thay vì chuyển một lần thật lớn không
    • Đây không chỉ là vấn đề của npm. Vấn đề một dependency nào đó lại phụ thuộc vào phiên bản cũ thông qua dependency bắc cầu khác thực tế xảy ra trong hầu hết mọi hệ sinh thái
      Ngược lại, npm còn có nhiều lối thoát hơn vì khi cần có thể chạy đồng thời nhiều phiên bản, hoặc ghi đè có chọn lọc một phần của đồ thị dependency bắc cầu
      Hệ thống quản lý package nào giải quyết được vấn đề này? Ngay cả các nền tảng “ổn định” như Maven cũng xử lý kiểu vấn đề này bằng cách phát hành phiên bản dưới namespace mới, như khi Apache Commons chuyển từ v2 sang v3
  • Có một câu hỏi tôi đã thắc mắc từ lâu. Tôi nghe nói Zod có thể phù hợp với việc này, nhưng đọc tài liệu vẫn không rõ phải làm thế nào
    Giả sử API máy chủ trả về các kiểu tinh vi hơn những gì nó có thể biểu diễn. Ví dụ api/:postid/author trả về User, nhưng có thể là User thông thường hoặc User ẩn danh, nên các trường như username, location có thể là null. Trong trường hợp này, có thể bạn sẽ muốn biểu diễn đối tượng User bằng discriminated union
    Các đối tượng đến từ endpoint khác cũng có thể cần chuyển đổi kiểu. User đôi khi có thể bao gồm Post[], và nếu Post là bài viết của moderator thì cũng có thể có thuộc tính đặc biệt
    Trước đây tôi từng viết các hàm như normalizeUser()normalizePost(), nhưng rất nhanh trở nên lộn xộn. Mỗi endpoint trả về một tập con khác nhau của model User/Post, khiến tôi phải tạo chừng 5 hàm normalizePost, quá rối. Tôi tò mò thường thì vấn đề kiểu này được giải quyết thế nào

    • Có vẻ dùng discriminated union là được
      Tức là tách các đối tượng successfailed dựa trên một trường phân biệt như status. Nếu có nhiều thuộc tính đặc biệt cho moderator nhưng bạn không muốn liệt kê hoặc kiểm tra tất cả, bạn có thể định nghĩa hành vi cho phép đi qua
      Dù nhiều method có schema khác nhau, nếu có phần chung thì hãy tạo schema đối tượng dùng chung, rồi tạo các đối tượng bổ sung thêm trường lên đó. Nếu có nhiều khả năng thì vẫn sẽ lộn xộn, nhưng nếu tình hình vốn đã lộn xộn thì validator ít nhất có thể kiểm chứng được sự lộn xộn đó
    • Lý tưởng nhất là nên có single source of truth cho việc User có thể có những hình dạng nào. Đó có thể là một discriminated union như User và AnonymousUser
      Nếu không phải full-stack TypeScript, chẳng hạn backend Python, có thể định nghĩa nhiều dạng User bằng Pydantic model và union, rồi tạo kiểu TypeScript cho client thông qua việc sinh schema OpenAPI/GraphQL và code generation
    • Cần biết thêm use case mới giải quyết được, nhưng nếu thêm một thuộc tính phân biệt như "user_type" vào mọi kiểu trong union thì sẽ dễ xử lý trường hợp thường và trường hợp đặc biệt hơn
      Ví dụ, nếu kiểm tra user.user_type === 'authenticated', hệ thống kiểu sẽ biết rằng sau đó có thể dùng user.name
    • Điều này không giúp trực tiếp cho trường hợp hiện tại, nhưng GraphQL được tạo ra cho những việc như thế này
      Các thư viện TypeScript cho truy vấn GraphQL có thể suy luận động dạng phản hồi từ hình dạng các trường đã chọn. Nếu chọn { user { username posts { text } } } thì kiểu phản hồi sẽ có posts, còn nếu chỉ chọn { user { username } } thì thuộc tính posts sẽ hoàn toàn vắng mặt
    • Máy chủ phải xuất kiểu ra. Việc viết tay kiểu ở client là vô lý
      Máy chủ biết dữ liệu mình gửi, nên phải cung cấp thông tin đó cho client dưới dạng metadata. Trên thực tế, có thể là backend Python dùng schema Pydantic, dựa vào đó tự động tạo đặc tả OpenAPI, rồi client sinh kiểu từ đó
  • Zod tốt hơn hẳn các lựa chọn thay thế tôi từng thấy, nhưng đôi khi chính việc cần kiểm chứng tường minh như thế này lại khiến tôi cảm thấy đó là một thất bại về việc phát triển web hiện đại đã đi đến đâu
    Trong phát triển full-stack, việc phải mô tả cùng một hình dạng theo nhiều cách thật quá bực bội. Cuối cùng cần kiểm chứng input JS, Swagger để định nghĩa API, kiểm chứng input phía máy chủ, ORM để đảm bảo phù hợp schema, rồi cả các định nghĩa TypeScript riêng cho server và client, rất nhàm chán

    • Việc TypeScript khăng khăng là một hệ thống chỉ dành cho kiểm tra tĩnh/compile time là một vết thương đáng tiếc cho cả hệ sinh thái. Tôi không mong TypeScript trở thành runtime checker, nhưng mong nó xuất ra dữ liệu kiểu có thể dùng hữu ích cho class, function và object
      TypeScript có vẻ như là single source of truth tốt nhất mà chúng ta có, nhưng thực tế các công cụ thử reflection lại tự định nghĩa lại model và builder của riêng mình. Chỉ riêng các lĩnh vực lớn lấy reflection làm cốt lõi đã có 5 mảng, mỗi mảng lại có nhiều cách triển khai
      Công cụ phát TypeScript type cũ là reflect-metadata cung cấp một phần thông tin kiểu ở runtime, nhưng nó nhắm tới đặc tả decorator và metadata rất cũ: https://www.npmjs.com/package/reflect-metadata
      Có lẽ chúng ta đang ở giai đoạn đầu của sự hợp nhất. Dự án Standard Schema dường như được phần lớn các thư viện kiểm chứng lớn hỗ trợ. Tuy vậy, việc mở rộng nó sang định nghĩa API hay công cụ ORM vẫn còn rất sơ khai
      https://github.com/standard-schema/standard-schema?tab=readm...
    • Thực ra đây chính là cốt lõi của loại công cụ này. Chỉ cần định nghĩa một lần, rồi sinh động mọi thứ bên dưới
      Chỉ cần thay đổi một lần trong schema Zod là nó lan ra toàn bộ ứng dụng cùng với kiểm tra kiểu. Schema Zod trở thành single source of truth
    • Thông thường không cần định nghĩa lại dữ liệu ở khắp nơi. Có rất nhiều công cụ chuyển sang Zod hoặc chuyển từ Zod
      Nếu đã có JSON Schema/Swagger schema thì cứ sinh Zod từ đó. Nếu dùng TypeScript ORM thì gần như chắc chắn sẽ có generator cho Zod
      Thành thật mà nói, Zod được dùng rộng đến mức với tôi nó trở thành định nghĩa schema thống nhất mà những phần khác trong stack có thể phụ thuộc vào. Vì định nghĩa kiểu trực tiếp nên các developer thực sự giữ nó luôn cập nhật. Tài liệu Swagger của công ty thì luôn tụt hậu so với thay đổi
    • Mọi API đều mang tính thử nghiệm và luôn trong trạng thái thay đổi, tiến hóa; web cũng vậy
      Trường hợp giữ nguyên hiện trạng là ổn chỉ tồn tại khi làm việc với khách hàng và nhà tuyển dụng chỉ muốn kết quả, không quan tâm cách làm. Trong bối cảnh đó, những tầng phức tạp đáng buồn này ít nhất cũng giúp tăng số giờ có thể tính phí
    • Nhiều người than phiền rằng phát triển web hiện đại phức tạp có lẽ cũng sẽ ghét đề xuất thay tất cả những thứ này bằng TypeScript và Zod
      Tất nhiên với tiền đề rằng Zod là thư viện định nghĩa schema và kiểm chứng TypeScript mà họ đã chọn
  • Zod 4 trông có vẻ tốt, nhưng ngay cả khi tính cả các cải tiến mới nhất thì ArkType vẫn nhanh hơn nhiều lần, ở mức một chữ số
    Vì tương thích ngược và tương thích cú pháp, đôi khi rất khó làm cho nó nhanh hơn nhiều như một thư viện khởi đầu hoàn toàn mới. Gần đây tôi đã phân tích tất cả các công cụ này trong một dự án và chọn ArkType một phần vì lý do đó. Ngoài ra cảm giác dùng TypeScript cũng tốt hơn

    • Tôi đã xem các chỉ số tốc độ, nhưng tò mò liệu có thể giải thích tốc độ đó tạo ra khác biệt ở đâu không
      Chúng tôi chỉ dùng Zod để kiểm tra form nên tôi nghĩ “vì sao chuyện này lại quan trọng?”. Có phải có trường hợp mọi người kiểm tra những thứ như thông điệp đầu vào API thông lượng cao, khiến hiệu năng quan trọng hơn không
    • Tò mò vì sao bạn chọn ArkType thay vì TypeBox
    • Thú vị là khi khảo sát tôi thậm chí không thấy công cụ đó. Tôi cũng đặc biệt xem xét cảm giác dùng TypeScript
      Dù vậy có lẽ tôi sẽ không chuyển khỏi Zod
    • ArkType là ác mộng khi viết, còn Zod thì dễ viết
  • Chúc mừng đội Zod với bản phát hành mới. Dù có nguy cơ nghe quá tiêu cực, tôi vẫn rùng mình khi nghĩ đến số lượng thay đổi phá vỡ tương thích được ghi trong hướng dẫn migration
    Nếu là một dự án phụ thuộc nhiều vào Zod, đây có vẻ sẽ là một công việc nặng nề, đòi hỏi nhiều sự tập trung và thời gian của lập trình viên. Tôi đặc biệt đồng cảm vì từng bảo trì vài dự án frontend 4–5 năm tuổi ở công ty
    Các dự án React lớn thường phụ thuộc vào nhiều thư viện, và mỗi khi từng thư viện đưa ra thay đổi lớn, lại còn thiếu tài liệu, mọi thứ rất nhanh trở nên quá tải. Đây là một trong những phần tôi ghét nhất khi làm việc với JavaScript, và việc giữ cho mọi thứ khớp nhau để tiếp tục chạy trơn tru giống như một con dốc leo mãi không hết

    • Hoàn toàn đồng ý. Tôi vận hành vài app Next.js lớn, và trong năm qua đã phải trải qua thay đổi cache quy mô lớn từ Next.js 14 → 15, Next.js pages → app router, React 18 → 19, Eslint 8 → 9, Tailwind 3 → 4
      Thành thật mà nói đó là ác mộng, và tôi đã nghĩ giá mà làm bằng Django thì tốt hơn. Trái với dự đoán, migration Tailwind 3 → 4 thuộc nhóm đau đớn nhất
    • Vì vậy họ chọn cách cung cấp kép cùng với bản mini riêng. Việc migration dần dần mà không động đến package manager trở nên dễ hơn
      Người dùng không quan tâm đến bản mini thì không cần để ý phần đó. Tuy nhiên lợi ích tree-shaking của bản mini quá lớn, đến mức đang kích thích phát triển các lựa chọn thay thế, và Zod phải chấp nhận điều đó hoặc suy thoái
    • Cứ dùng LLM là được
  • Là một full-stack developer tự vận hành một SaaS có hàng nghìn người dùng trong production, đôi khi tôi thấy khá dễ chịu khi nhận ra có biết bao vấn đề mà mình hoàn toàn không biết đến vì đã quyết định không làm SPA và không dùng framework frontend JS
    Tôi thậm chí chưa từng nghĩ mình cần những thứ như Zod/ArkType, đây là lần đầu nghe đến và cũng sẽ không dùng
    Tôi ngạc nhiên vì có quá nhiều công sức, độ phức tạp và công cụ được đưa vào việc xây SPA. Khi quyết định không làm SPA, cả một nhóm vấn đề nhất định đơn giản là không tồn tại. Thay vào đó, dùng trình duyệt đúng như cách ban đầu nó được thiết kế, với tải lại toàn trang và trọng tâm là phát triển backend, rồi thỉnh thoảng thêm tính phản ứng bằng các framework chỉ dành cho backend như Laravel Livewire
    Mọi thứ từ kiểm soát truy cập đến kiểm tra dữ liệu, quản lý trạng thái đều đơn giản. Ứng dụng nhanh, responsive, hiện đại, thân thiện SEO và đang phục vụ hàng nghìn người trong production

    • Trong hai dự án lớn có hơn 5 nhóm cùng làm việc, chúng tôi dùng Zod và Protobuf để schema evolution diễn ra tốt. Ngay cả với frontend nhẹ, việc dùng một thứ gì đó để kiểm tra schema ở backend vẫn có ý nghĩa
      Rốt cuộc thì schema nằm ở đâu đó. Nó có thể được định nghĩa trong hệ thống cơ sở dữ liệu, hoặc trong model ORM. Nhưng trong dự án lớn, đặt schema trong hệ thống cơ sở dữ liệu hoặc ORM có thể tạo ra những vấn đề rất khó, nên chúng tôi dùng Zod và Protobuf. Tôi nghĩ Protobuf cũng có thể được thay thế hoàn toàn bằng Zod hoặc thứ tương tự
      Vụ bê bối Post Office ở Anh có liên quan đến một hệ thống IT tên Horizon: https://en.wikipedia.org/wiki/British_Post_Office_scandal
      Sau khi đọc chi tiết, tôi nghĩ nếu có kiểm tra schema như Zod thì có lẽ đã góp phần ngăn vụ bê bối này. Phụ lục kỹ thuật trong tài liệu tòa án của vụ kiện tập thể cũng nhắc đến việc thiếu sử dụng schema: https://en.wikipedia.org/wiki/Bates_%26_Others_v_Post_Office...
    • Zod không liên quan đến SPA và cũng không phải công cụ chỉ dành cho frontend. Bạn không kiểm tra đầu vào trong code à?
    • Nếu nhắm mắt lại thì đôi khi cả một nhóm vấn đề cũng biến mất
    • Tò mò bạn kiểm tra dữ liệu phía server như thế nào
      Nghe như hiện tại bạn đang phòng thủ bằng cách viết rất nhiều câu lệnh if. Tệ hơn nữa là có thể chỉ dựa vào kiểm tra form phía client của HTML
    • Chúng tôi có rất nhiều schema Zod chỉ dùng ở backend như một phần của data pipeline. Vì vậy nó không giống một công cụ chỉ tồn tại để giải quyết vấn đề SPA
  • Tôi không phải chuyên gia nên nói hơi thận trọng, nhưng tôi nghĩ JSON Schema có thể là một lựa chọn tốt vì nó dựa trên schema, nên có thể triển khai trình xác thực cả trong các ngôn ngữ không phải TypeScript
    https://ajv.js.org là một trong những thư viện JSON Schema như vậy. Tôi tò mò không biết so với Zod thì thế nào

    • Zod không phải là công cụ chỉ dành cho xác thực JSON. Nó có thể xác thực các đối tượng không thể biểu diễn bằng JSON nếu không chuyển đổi, chẳng hạn như ngày tháng hoặc instance của class
      Nếu muốn, cũng có thể dùng nó như một bộ chuyển đổi JSON. Ví dụ, bạn có thể viết một schema nhận vào chuỗi, xác thực xem đó có phải chuỗi ngày ISO hay không, rồi xuất ra đối tượng Date
    • Zod 4 hỗ trợ native khả năng chuyển đổi schema Zod sang JSON Schema. Trước đây cũng có thể làm được bằng thư viện bên thứ ba
      Khác biệt quan trọng là tiền xử lý và refine. Trong Zod, bạn có thể cung cấp callback trước khi xác thực, điều này rất hữu ích nhưng không thể biểu diễn bằng JSON. Ví dụ, việc đổi MM/DD/YYYY thành DD/MM/YYYY trước khi xác thực ngày tháng hữu ích thường xuyên hơn bạn tưởng
    • Trong những trường hợp như vậy, JSON Schema là lựa chọn tốt, và TypeBox là một cách khá ổn để tạo ra nó
      Khi xác thực, có thể dùng AJV hoặc hệ thống xác thực schema của chính TypeBox. Cái sau nhanh hơn nhiều nhưng chỉ hỗ trợ đồng bộ, còn AJV có các tùy chọn xác thực bất đồng bộ như kiểm tra cơ sở dữ liệu
    • Với xác thực giữa các ngôn ngữ, JSON Schema là phù hợp. Nếu bọc nó bằng OpenAPI, bạn còn có được tài liệu API khá ổn gần như miễn phí
  • Tôi ngạc nhiên trước các phản ứng tiêu cực xung quanh chủ đề này
    Tôi đã thử nghiệm phiên bản đầu của Zod v4 và thích API mới, nhưng rất lo lắng không biết lộ trình migration sẽ ra sao. Tôi thậm chí đã định đề xuất phát hành dưới một tên package hoàn toàn mới
    Tuy nhiên, cách tiếp cận của tác giả rất khôn ngoan. Nó cho phép những người như tôi áp dụng v4 ngay lập tức mà không cần chờ mọi dependency được cập nhật

    • Đây là một lộ trình tốt. Với những thứ như xác thực, không có cách nào chịu nổi kiểu migration được ăn cả ngã về không
  • Tôi vừa bắt đầu đưa Zod vào một dự án mới, và không thể có thời điểm nào tốt hơn
    Dựa trên những gì thấy hiện tại, có vẻ nếu chuyển sang v4 thì đã phải thay đổi rất nhiều thứ