Lucrăm la optimizarea unui API TypeScript (Hono + Cloudflare Workers și câteva Server Actions) care a adunat în jur de 50 de scheme de validare destul de stufoase. În momentul de față folosim Zod peste tot, dar pe măsură ce proiectul a crescut, am început să simt greutatea pachetului la fiecare deploy.
Nu e un capăt de țară, dar caut soluții înainte ca datoria tehnică să devină de nerezolvat.
Problema cu Zod în arhitecturi edge / serverless
Zod este excelent ca DX. Îl folosesc de vreo 4 ani și API-ul lui fluent bazat pe method chaining (z.string().min(3).optional()) e a doua natură pentru mine. Totuși, arhitectura lui orientată pe clase și chaining face tree-shaking-ul practic imposibil.
La un audit recent pe bundle-ul de producție, am observat că runtime-ul Zod ocupă undeva la ~58KB minified (în jur de 14KB gzipped) doar ca bază, indiferent dacă folosesc 3 metode sau 50. Pe un VPS clasic nu-ți pasă, dar pe Workers și lambdas unde avem 200k+ request-uri pe zi, cold start-ul pe endpoint-urile izolate a crescut de la 70ms la 210ms pe măsură ce importurile de scheme s-au acumulat.
Ce promite Valibot și unde am dubii
Valibot merge pe o abordare complet funcțională, similară cu date-fns vs moment.js. În loc de chaining, folosești funcții compuse cu v.pipe(). Conform analizelor teoretice, pentru setul nostru de reguli am putea reduce amprenta la 4-6KB, adică o scădere de aproape 85-90% din greutatea validării.
Trade-off-ul e clar la nivel de sintaxă și ergonomie: codul devine ceva mai verbose, iar lizibilitatea schemelor imbricate scade puțin când ai multe transformări și .pipe()-uri consecutive.
Trade-off-ul ascuns – și motivul pentru care deschid acest thread – ține de ecosistem și edge case-uri:
- Generarea de OpenAPI / Swagger: În Zod foloseam
@asteasolutions/zod-to-openapiși totul mergea brici. La Valibot există alternative (cum ar fi@valibot/to-json-schema), dar nu știu cât de bine se descurcă în realitate cu discriminated unions complexe. - Transformări asincrone și refine-uri: Avem câteva validări care verifică unicitatea unor slug-uri prin apeluri la DB direct în schemă (
v.checkAsync/z.refineasync). Cum se comportă Valibot la erori paralele vs secvențiale? - Compatibilitatea cu tipurile inferate: Cât de curat rămâne
v.InferOutput<typeof Schema>în comparație cuz.infer<typeof Schema>pe tipuri recursive?
Ce am testat până acum
Am rescris de test 4 scheme complexe (un formular de checkout cu date de facturare condiționate și 3 endpoint-uri de CRUD). Bundle-ul per worker a scăzut într-adevăr vizibil, iar execuția pură la parse pare cu vreo 25-30% mai rapidă în micro-benchmark-uri locale.
Totuși, la rescrierea manuală a 50 de fișiere estimez cam 2-3 zile de muncă migăloasă + refăcut teste unitare. Încă nu sunt 100% convins dacă raportul efort/beneficiu merită sau dacă n-ar trebui pur și simplu să accept overhead-ul de la Zod și să optimizez alte părți din pipeline.
A făcut cineva dintre voi trecerea completă pe un proiect aflat deja în producție? Ați lovit blocaje neașteptate la validarea erorilor formatate pentru frontend sau migrarea a fost chiar atât de lină pe cât zice documentația?