5 puntos por GN⁺ 2024-05-05 | 1 comentarios | Compartir por WhatsApp
  • Figma migró todo su código de Skew, su lenguaje propio usado para el renderizado móvil y el visor de prototipos, a TypeScript cuando Skew empezó a topar con límites en onboarding, integración y ecosistema
  • Las condiciones para la transición se dieron con la expansión del soporte de WebAssembly en navegadores móviles, el reemplazo del motor en rutas críticas por uno en C++, y el crecimiento de los equipos de prototipado y móvil
  • La migración avanzó en 3 etapas: Write Skew, build SkewWrite Skew, build TypeScriptWrite TypeScript, build TypeScript, y un transpilador de Skew a TypeScript mantuvo el flujo de desarrollo
  • En la migración real aparecieron problemas de compatibilidad, como el rendimiento de la desestructuración de arreglos, diferencias en la eliminación de llamadas virtuales (devirtualization), el orden de inicialización, el enlace de source maps y la ausencia de compilación condicional
  • Al final obtuvieron una base de código en TypeScript que pasaba las pruebas unitarias y ofrecía un rendimiento similar al de Skew, y Figma ve como siguientes tareas la integración de código interno y externo, la gestión de paquetes y el aprovechamiento del ecosistema de TypeScript

Por qué se movieron de Skew a TypeScript

  • Skew nació como un proyecto paralelo en los inicios de Figma y cubría la necesidad de crear rápidamente un visor de prototipos compatible tanto con web como con móvil
  • A medida que evolucionó como un lenguaje que compila a JavaScript, ofreció optimizaciones avanzadas y tiempos de compilación rápidos, pero conforme se acumulaba código en el visor de prototipos, el costo de mantenimiento fue creciendo
    • Era difícil de aprender para quienes se incorporaban a la empresa
    • No se integraba fácilmente con el resto de la base de código
    • No existía un ecosistema de desarrolladores fuera de Figma
  • Con el cambio a TypeScript esperaban ampliar la velocidad de desarrollo y el alcance de la colaboración
    • Integración más simple de código interno y externo con imports estáticos y gestión nativa de paquetes
    • Uso de herramientas creadas por una comunidad grande de desarrolladores, como linters, bundlers y analizadores estáticos
    • Uso de funciones modernas de JavaScript como async/await y de un sistema de tipos más flexible
    • Menor fricción para el onboarding de nuevos desarrolladores y para la participación de otros equipos

Condiciones que hicieron posible la transición recién ahora

  • Cuando Figma creó por primera vez su base de código móvil, los navegadores móviles no soportaban WebAssembly y además era difícil cargar bundles grandes con buen rendimiento
  • TypeScript también estaba en una etapa temprana, así que Skew, con tipado estático y un sistema de tipos más estricto, era entonces una opción más realista
  • WebAssembly logró soporte amplio en móvil hacia 2018, y según las pruebas de Figma, en 2020 el rendimiento móvil llegó a un nivel confiable
  • Una fortaleza inicial de Skew era combinar optimizaciones clásicas de compiladores, como constant folding y eliminación de llamadas virtuales, con optimizaciones específicas para la web, como generar JavaScript usando operaciones reales con enteros
  • En benchmarks de 2020 se confirmó que cargar un prototipo de Figma en TypeScript en Safari podía ser casi 2 veces más lento, y como Safari era el único motor de navegador permitido en iOS, eso bloqueaba la transición
    • En iOS 17.4, Apple abrió otros motores de navegador para usuarios de la UE, pero para usuarios de otras regiones WebKit sigue siendo el único motor disponible
  • Después, muchos componentes centrales del motor de Skew fueron reemplazados por sus equivalentes en el motor de C++ y eso redujo el riesgo de rendimiento de pasar a TypeScript
    • Incluía las rutas de código más críticas, como la carga de archivos
    • Eso les dio confianza de que podían absorber la pérdida de rendimiento al migrar a TypeScript
  • A medida que los equipos de prototipado y móvil crecieron hasta convertirse en una organización más grande, pudieron dedicar recursos a la migración automática y a mejorar la experiencia del desarrollador

Migración de la base de código en 3 etapas

  • En el primer prototipo de migración de 2020, el rendimiento de TypeScript era casi 2 veces más lento
  • Una vez que el soporte de WebAssembly y la migración del motor móvil a C++ fueron suficientes, Figma aprovechó una Maker Week para corregir el prototipo existente y demostrar una migración funcional que pasaba todas las pruebas
  • El objetivo era convertir toda la base de código a TypeScript, pero reescribirla manualmente podía bajar la velocidad de desarrollo y aumentar el riesgo de errores en runtime y de degradación de rendimiento
  • Skew y TypeScript tenían diferencias semánticas mayores que una simple transición entre variantes de “JavaScript con tipos”
    • En TypeScript, un namespace y una clase solo se inicializan después de importar el archivo
    • Si el orden de los imports no coincide con lo esperado, pueden aparecer errores en runtime
    • Skew hace que todos los símbolos estén disponibles en runtime en toda la base de código al momento de cargar
  • Figma desarrolló un transpilador de Skew a TypeScript a partir de trabajo previo que Evan Wallace había comenzado tiempo atrás
  • Fase 1: Escribir Skew y compilar Skew

    • Desarrollaron el transpilador manteniendo el proceso de build existente
    • Hicieron commit del código TypeScript generado en GitHub para que los desarrolladores pudieran ver la nueva forma de la base de código
  • Fase 2: Escribir Skew y compilar TypeScript

    • Una vez que pudieron generar un bundle de TypeScript que pasaba todas las pruebas unitarias, fueron desplegando gradualmente tráfico de producción construido desde la base de código TypeScript
    • Los desarrolladores seguían escribiendo en Skew, y el transpilador convertía ese código a TypeScript y actualizaba el código TypeScript en GitHub
    • También siguieron corrigiendo errores de tipos en el código generado
    • TypeScript podía generar un bundle válido incluso con errores de tipos
  • Fase 3: Escribir TypeScript y compilar TypeScript

    • Después de que todos los usuarios ya pasaban por el proceso de build de TypeScript, cambiaron el código TypeScript para que fuera la única fuente de verdad del desarrollo
    • Buscaron un momento sin merges de código, detuvieron el proceso de generación automática y eliminaron el código Skew de la base de código
    • El viernes por la noche hicieron merge de la eliminación de la autogeneración y del cambio en trabajos de CI para ejecutar directamente archivos TypeScript
    • Durante el despliegue gradual detectaron internamente una falla en Smart Animate, y gracias al despliegue con gating pudieron apagar el rollout, corregirlo y revisar el plan

Problemas que salieron a la luz en el transpilador

  • Un compilador suele estar compuesto por frontend y backend
    • El frontend analiza el código de entrada, realiza type checking y revisión sintáctica, y luego lo convierte a una representación intermedia (IR)
    • El backend transforma esa IR a otro lenguaje
    • El backend del compilador de Skew generaba JavaScript ofuscado y minimizado
  • Un transpilador es un compilador especial cuyo backend genera código legible por humanos, y Figma necesitaba generar TypeScript legible a partir de la IR de Skew
  • La implementación inicial fue relativamente sencilla porque tomó mucha inspiración del backend de JavaScript de Skew, pero más adelante surgieron varios problemas difíciles de rastrear y procesar
  • Rendimiento de la desestructuración de arreglos

    • Mientras investigaban la diferencia de rendimiento offline entre Skew y TypeScript en un prototipo de muestra, vieron que TypeScript tenía una tasa de cuadros más baja
    • La causa fue la desestructuración de arreglos de JavaScript
    • En operaciones como const [a, b] = function_that_returns_an_array(), JavaScript no indexa directamente el arreglo, sino que crea un iterador y lo recorre
    • Figma usaba este patrón para obtener argumentos desde la palabra clave arguments de JavaScript, y eso volvía lento el rendimiento en ciertos casos de prueba
    • Generar código que indexaba directamente el arreglo de argumentos en vez de desestructurarlo mejoró la latencia por frame hasta en 25%
  • Optimización de eliminación de llamadas virtuales en Skew

    • El compilador de Skew aplica una optimización de devirtualization que, en ciertas condiciones, saca funciones fuera de la clase y las eleva a funciones globales
    • myObject.myFunc(a, b) puede convertirse en myFunc(myObject, a, b)
    • TypeScript no hace esta optimización
    • La falla en Smart Animate ocurría cuando myObject era null
    • La llamada devirtualizada se ejecutaba con normalidad, pero la no devirtualizada lanzaba una excepción por acceso a null
    • Para encontrar otros puntos de llamada con el mismo problema, Figma agregó logging a todas las funciones que podían participar en la devirtualization
    • Tras activar el logging por un periodo corto, analizaron los logs de producción y corrigieron los puntos de llamada problemáticos
  • Diferencias en el orden de inicialización

    • En Skew, las definiciones de variables, clases, namespaces y funciones pueden declararse en cualquier lugar del código sin preocuparse por el orden
    • En TypeScript, el orden de inicialización de variables globales o definiciones de clases sí importa
    • Si se inicializa antes una variable estática de clase que la definición de la clase, aparece un error en tiempo de compilación
    • El transpilador inicial no usaba namespaces y aplanaba todas las funciones al scope global para conservar un comportamiento parecido al de Skew
    • El resultado era código difícil de leer, así que después corrigieron el transpilador para emitir código TypeScript en el orden correcto y reintrodujeron namespaces por legibilidad
    • Al final lograron un transpilador capaz de compilar código TypeScript que pasaba las pruebas unitarias y mantenía el rendimiento de Skew
    • Algunos problemas menores se resolvieron directamente en el código fuente de Skew o después del cambio a TypeScript, en vez de agregar más parches al transpilador

Mantener la experiencia de depuración con source maps

  • Figma dio mucha importancia a los source maps para que los desarrolladores pudieran depurar sin interrupciones durante la transición a TypeScript
  • El depurador del navegador solo entiende JavaScript, pero los desarrolladores colocan breakpoints en el código fuente Skew o TypeScript
  • Los source maps conectan una ubicación en el JavaScript compilado con una ubicación en el código fuente original
    • Por ejemplo, se pueden hacer mapeos como helper → c, myInt → a, arrayOfInts → b
  • Normalmente, un archivo source map con extensión .map se enlaza a un único bundle final de JavaScript
  • La infraestructura existente generaba source maps de Skew → JavaScript, pero en la Fase 2 el pipeline cambió al flujo Skew → TypeScript → bundling con esbuild
  • Si se usaban los source maps existentes tal cual, el mapeo entre JavaScript y código Skew dejaba de coincidir, y los desarrolladores ya no podían depurar
  • El nuevo proceso de build armó los source maps en 3 pasos
    • Paso 1: esbuild genera el source map TypeScript → JavaScript ts-to-js.map
    • Paso 2: el transpilador genera un source map Skew → TypeScript para cada archivo Skew
    • Paso 3: se componen ambos source maps para generar el source map final Skew → JavaScript
  • Con el source map final pudieron mapear el nuevo bundle JavaScript al código Skew y mantener la experiencia de desarrollo también en la Fase 2

Cómo resolvieron la compilación condicional en TypeScript

  • Skew soporta compilación condicional con sentencias if de nivel superior y con la opción defines que se pasa al compilador
  • Esa función permitía construir distintos bundles desde una misma base de código
    • El bundle real que se distribuye a usuarios
    • Un bundle exclusivo para pruebas unitarias
    • Funciones o clases que usan implementaciones distintas en builds debug o release
  • Como TypeScript no tiene compilación condicional, Figma usó defines de esbuild y dead code elimination después del type checking para hacer la compilación condicional en la etapa de bundling
  • En este enfoque, defines no puede afectar el type checking
    • Código como testOnlyFunction, que solo existe cuando BUILD == "TEST", no puede existir así tal cual en TypeScript
  • La solución fue transformar eso para que las clases y métodos siempre estén definidos, y que el branching según el valor de BUILD ocurra dentro del método
    • Si testOnlyFunction se llama en un build que no es de pruebas, lanza el error Unexpected call to test-only function
  • El JavaScript final podía compilarse de forma equivalente al JavaScript que generaba originalmente el código Skew
  • Aun así, algunos símbolos que antes solo existían en ciertos modos de compilación pasaron a existir en todos los modos, lo que hizo el bundle final ligeramente más grande
  • En pruebas, el aumento de tamaño del bundle fue aceptable, y los símbolos de nivel superior no exportados podían eliminarse con tree-shaking

Dirección después de la transición

  • Al mover todo el código de Skew a TypeScript, Figma modernizó una de sus bases de código centrales
  • Se volvió más fácil integrarse con código interno y externo, y los desarrolladores pudieron trabajar con mayor eficiencia
  • Para las necesidades y capacidades que tenía Figma en ese momento, la decisión de iniciar esa base de código con Skew fue adecuada
  • A medida que la tecnología maduró, TypeScript, que antes no era una opción apropiada, pasó a serlo
  • Como siguiente paso, Figma explora posibilidades como
    • Integración con el resto de la base de código
    • Gestión de paquetes mucho más sencilla
    • Uso directo de nuevas capacidades del activo ecosistema de TypeScript
  • En el proceso de transición aprendieron varios aspectos de TypeScript, como la resolución de imports, el sistema de módulos y la generación de código JavaScript

1 comentarios

 
GN⁺ 2024-05-05
Opiniones de Hacker News
  • Ya es sorprendente que Figma tuviera un lenguaje personalizado para JS, y todavía más sorprendente que fuera más rápido que TS.
    Pero también es interesante que al final se hayan pasado a TS, que es más lento.
    Esto se ve con bastante frecuencia: una empresa crea su propia tecnología al inicio y, cuando crece, migra a algo más “estándar”.

    • Me parece un patrón bastante típico. Al principio, una persona brillante como Evan configura toda la cadena de herramientas y el núcleo del producto, y después se va, queda desplazada o pierde interés.
      Cuando el equipo y el producto crecen mucho más, vuelven a llevar la plataforma a un stack más familiar y ampliamente usado.
      El éxito de una transición así depende mucho de qué tan sólida sea la cultura de ingeniería de la organización. No tengo pruebas, pero parece que Evan y los fundadores dejaron una gran cultura de ingeniería en Figma, y que tienen suficiente resiliencia para corregir los errores que aparezcan.
    • Si lees con más detalle, todas las partes sensibles al rendimiento se trasladaron a C++/WASM.
      Para ese momento solo quedaba el código de pegamento, y el lenguaje personalizado ya no tenía razón de existir.
    • En la comunidad PHP pasó algo parecido. Facebook es el caso representativo, pero Yahoo también tenía bastantes optimizaciones internas, y vi otros casos de empresas que lo ajustaban por su cuenta por rendimiento o seguridad.
      Luego llega un momento en que la comunidad se pone al día y gana más impulso que la empresa, y entonces tiene sentido pasarse a la implementación estándar.
      También veo algo similar en el recorrido de Borg de Google hacia k8s. Aunque, en el caso de Google, más bien invitó a la comunidad a sumarse alrededor de un estándar que ellos mismos crearon.
    • Que sea más rápido que TS en un uso estrecho es una vara relativamente baja. Pero a medida que se escala, esa vara hay que evaluarla junto con el pool de desarrolladores disponible en el mercado.
      Hay que comparar el costo de un desarrollador que puede ver por primera vez un framework completamente nuevo y aprenderlo, con el valor de un desarrollador que ya tiene experiencia documentada en una plataforma o tecnología en uso.
      Si vas por algo totalmente desarrollado internamente, puedes exprimir un rendimiento enorme de cualquier lenguaje o framework, pero a medida que el proyecto necesita cubrir más situaciones, aumenta la cantidad de desarrolladores necesaria para mantenerlo, y termina llegando un punto en que el software comercial ya hecho resulta mejor, aunque sea lento y no encaje exactamente con los requisitos.
      Si a largo plazo puedes mantener criterios de contratación de nivel unicornio, quizá puedas sostener un stack completamente personalizado. Pero la mayoría de las organizaciones tiene que moverse hacia algo que una contratación nueva promedio pueda mantener en el futuro, y por lo general eso significa productos de proveedores de software conocidos y aburridos.
    • Algo raro y peculiar como un lenguaje personalizado es perjudicial para el CV si no eres quien lo implementa, sino solo un usuario, por ejemplo un desarrollador que usa ese lenguaje.
      Para alguien ambicioso puede ser un motivo para irse, también dificulta la contratación y alarga el onboarding. Más adelante, cuando se abandona el lenguaje personalizado, también hace falta un gran proyecto de migración.
      A veces puede valer la pena si la mejora de rendimiento es enorme, si permite escribir código más seguro o si la intención es contratar solo a gente a la que le guste aprender lenguajes nuevos.
  • Trabajé en este proyecto. Dejé un poco más escrito en Twitter: https://twitter.com/andrew_k_chan/status/1786769203912925477
    El título del artículo es engañoso. En Figma se usó Typescript durante casi 10 años en otras partes del codebase, y durante casi todo ese tiempo hubo más código Typescript que Skew.
    Como explica el post del blog, Skew se usó en el motor móvil, después en el reproductor de prototipos, en la función de mirroring y en una o dos áreas de producto que ya no recuerdo.

  • Skew no era apenas un poco más rápido que TypeScript.
    Según Evan Wallace, ex CTO de Figma, gracias a las optimizaciones que permitía un sistema de tipos más estricto, era entre 1.5 y 2 veces más rápido.

    • Es una lástima que los navegadores no hayan seguido desarrollando el modo "use strong" de JS.
      Parece que había desafíos bastante grandes, pero reducir algo de dinamismo a cambio de optimizaciones más predecibles suena como un buen compromiso para apps de producción.
    • La afirmación de que “era entre 1.5 y 2 veces más rápido gracias a un sistema de tipos más estricto” probablemente no sea tan simple.
      Si optimizas las rutas de ejecución calientes, es bastante probable que esa ventaja de 2 veces desaparezca.
      Claro que entonces tendrías que escribir JS optimizado a la fuerza en varios lugares, y se podría decir que eso afecta la legibilidad. Es cierto, pero la alternativa era usar un lenguaje completamente nuevo que compilaba a JS, con menor soporte de herramientas y menor reconocimiento.
      Visto ahora, parece que no valía la pena, y el post del blog da la impresión de estar presentando de forma algo favorable una decisión técnica anterior que fue controvertida.
    • La diferencia de velocidad de 2 veces fue lo más sorprendente de todo el artículo.
      Me pregunto si sería posible limitar el uso de TypeScript al subconjunto que logra ese rendimiento.
  • Es interesante que, al completar algo como const [a, b] = function_that_returns_an_array(), JavaScript cree un iterador que recorre el arreglo en vez de indexarlo directamente
    ¿Por qué JS no indexa directamente los arreglos en la asignación por desestructuración?

    • Cualquier objeto puede convertirse en un objeto iterable si se le agrega Symbol.iterator, y la desestructuración debe funcionar también con esos objetos. Incluso se puede parchear el Symbol.iterator del propio arreglo, así que la VM tiene que manejarlo
      Array.prototype[Symbol.iterator] = function*() { yield 1; yield 2; yield 3; }
      [...[4, 5, 6]]
      [1, 2, 3]
      El pésimo rendimiento del protocolo de iteradores ya se discutía en ese momento, pero se ignoró diciendo que el análisis de escape lo resolvería [0]. Casi 10 años después, el análisis de escape todavía no lo resolvió, usa muchísimo el GC y sigue siendo bastante malo. Es una mala especificación diseñada por gente que no piensa en el rendimiento
      Si el usuario no parcheó Symbol.iterator, tendría sentido que el motor especializara la desestructuración y el splicing de arreglos para eliminar la sobrecarga del protocolo de iteradores, pero eso es otro problema complicado
      [0] https://esdiscuss.org/topic/performance-of-iterator-next-as-...
    • Parece que es porque existen cosas como los proxies
      Lo más raro es que, en algunos motores de JavaScript, al desestructurar arreglos de dos o tres elementos, la desestructuración de objetos al estilo {0: foo, 1: bar} puede ser más rápida
  • Se habla de las transformaciones hechas una sola vez al migrar la base de código, pero casi no se trata el impacto continuo en la experiencia de desarrollo por perder las funciones de conveniencia de Skew
    Por ejemplo, en TypeScript es fácil terminar en casos donde hay que importar archivos en el orden correcto; de lo contrario, algo puede romperse. Además, la desestructuración es lenta, así que no debería usarse si el rendimiento importa aunque sea un poco
    Después de usar TypeScript por varios años, hay docenas de estas trampas; algunas heredadas de JavaScript y otras no. Si hay muchos ingenieros, como mínimo se necesita una guía de estilo enorme
    Me pregunto si hubo ingenieros que lamentaron que Skew desapareciera

    • Definitivamente era importante lograr que los desarrolladores quedaran conformes con el resultado. Por eso incluimos en el despliegue una etapa en la que los desarrolladores seguían escribiendo Skew, pero los cambios se registraban automáticamente como TS generado
      Así los desarrolladores podían ver en las revisiones de PR cómo se vería el resultado y reportar problemas
      En términos de rendimiento y corrección en tiempo de ejecución, es cierto que TS tiene varias trampas. Problemas como la desestructuración de arreglos los detectamos con instrumentación y monitoreo estricto
      Sin duda dolió perder algunas funciones de Skew, como la sobrecarga de operadores y los tipos enteros. Pero la migración fue, al final, una decisión tomada por todo el equipo, y yo también creo que fue la decisión correcta
  • Dice “funciones modernas de JavaScript como async/await y un sistema de tipos más flexible”; entonces, ¿Skew solo tenía callbacks?

    • ¿Quizás tenía Promise?
  • Como alguien que no conoce bien Figma, me da curiosidad por qué usan WebAssembly

    • Probablemente sepas que Figma es software de diseño UX. Es decir, básicamente es un programa gráfico. Dibuja formas, hace scroll y acerca/aleja
      Figma hace eso extremadamente bien. Incluso en lienzos enormes con muchas pantallas de UI complejas, se siente increíblemente ágil
      Hoy en día casi no hay aplicaciones de escritorio que funcionen tan bien. Estoy convencido de que esta optimización es una parte importante del éxito de Figma
    • La edición vectorial tiene muchas tareas ligadas a la CPU. Evan escribió sobre la transición a WASM: https://medium.com/figma-design/webassembly-cut-figmas-load-...
    • El objetivo inicial era crear Photoshop dentro del navegador
  • También hay detalles interesantes en otra entrada del blog sobre cómo Figma creó un DSL + compilador de TypeScript personalizado para resolver el problema de seguridad, es decir, los permisos
    https://www.figma.com/blog/how-we-rolled-out-our-own-permiss...

  • Se siente un poco doloroso. Cada empresa grande tiene sus propias herramientas internas, lenguajes, algo tipo Kubernetes, etc. ¿Por qué no los comparten?
    Si Skew hubiera sido open source, quizá podría haberse convertido en un mejor TypeScript

    • Skew es open source, pero ya no se mantiene: https://github.com/evanw/skew
    • No me gusta mucho este punto de vista; normalmente surge de la falta de experiencia con open source
      Que algo sea open source no significa que aparezcan contribuciones gratis. Cada PR que no sea trivial implica revisiones largas, discusiones y quizá hasta reescrituras
  • Me gusta TypeScript y también tengo sistemas full-stack hechos con TypeScript, pero no es perfecto. Configurar TypeScript en un monorepo es una pesadilla.
    Para que sea comprensible junto con los paquetes internos dentro de un monorepo con pnpm, hay que ajustar a mano muchos tsconfig.json para que todas las rutas coincidan entre sí.
    Y hasta que apareció el excelente paquete tsx, la cadena de herramientas de producción era prácticamente inmantenible.
    Además, es absurdamente lento. Por Zod, el rendimiento del servidor de lenguaje de TypeScript se ralentizaba mucho, así que al final tuvimos que introducir referencias de proyecto y desactivar la redirección de referencias de proyecto.
    En conjunto, hay mucho margen para mejorar TypeScript. Sobre todo en monorepos y en rendimiento.

    • Hay que tener en cuenta que Zod es un caso bastante excepcional en cuanto a cuánto puede ralentizar la verificación de tipos. Por ejemplo, una prueba en https://dev.to/nicklucas/typescript-runtime-validators-and-d... llega casi a 300 ms, mientras que otras bibliotecas casi ni llegan a 100 ms.
    • Zod es conocido por ser lento. Por ejemplo, Typebox es mucho más rápido. No digo que haya que cambiarse, sino que esto no es un problema de TypeScript ni algo que TypeScript tenga que resolver.
      El problema del monorepo suena a que no estaban usando la misma configuración en todos los paquetes. Si es así, primero arreglaría la imposición de la misma configuración y los mismos estándares de código. Eso tampoco es un problema de TypeScript.
      ¿Antes de tsx usaban ts-node? Si es así, tsx es mucho más sólido. Simplemente funciona bien.
    • No es que TypeScript tenga que soportar una combinación específica de herramientas y bibliotecas; al contrario, son las herramientas y bibliotecas las que deben adaptarse.
      Opero un monorepo mediano, varias apps y muchos servicios mayormente en TypeScript, y funciona razonablemente bien incluso con una configuración aburrida basada en npm workspaces.
    • Decir que TypeScript es lento porque Zod es lento es parecido a decir que C++ es lento porque JavaScript es lento.
      No quiere decir en absoluto que TypeScript sea rápido, pero como está escrito en JavaScript, también es difícil que lo sea.
      Dicho eso, dejar que TypeScript ejecute código para inferir tipos en una aplicación grande parece un problema autocreado.