Testlify funciona como un conjunto de 4 aplicaciones web separadas. Antes, cada equipo de producto desarrollaba y le daba estilo a sus propios componentes de interfaz de forma independiente, lo que causaba esfuerzo de ingeniería duplicado e inconsistencias visuales entre productos. Propuse la idea y lideré al equipo que construyó una biblioteca de componentes compartida en Vue 3 para estandarizar el desarrollo de la interfaz.
Lo que construí
- Consolidación arquitectónica: Lideré la iniciativa de consolidar las primitivas de interfaz comunes en una biblioteca centralizada, estableciendo tokens de diseño y contratos de componentes consistentes entre equipos.
- Diseño de componentes sin estado: Mantuve los componentes de la biblioteca estrictamente sin estado y presentacionales, lo que les permite integrarse limpiamente en cualquiera de las 4 aplicaciones mientras la lógica de negocio se mantiene desacoplada.
- Distribución como paquete privado: Publiqué y mantuve la biblioteca como paquetes versionados en GitHub Packages, lo que permite que los equipos adopten correcciones de errores y nuevos componentes mediante actualizaciones de dependencias estándar, sin duplicación de código.
Por qué importó
Centralizar el desarrollo de la interfaz en un paquete compartido eliminó código redundante en las 4 aplicaciones, aceleró los ciclos de entrega de funcionalidades, y aseguró un sistema de diseño coherente en toda la suite de productos de Testlify.
Liderado, no en solitario
Propuse la biblioteca y dirigí la arquitectura, pero no fue una construcción de una sola persona: 86 commits de 7 colaboradores desde mayo de 2026, con mis propios commits representando aproximadamente dos tercios del total. Conseguir que un sistema de diseño compartido se adopte en 4 equipos de producto necesita más que escribir los componentes; necesita el proceso de lanzamiento que hace que adoptarlos sea de bajo riesgo, lo cual se convirtió en el problema realmente difícil.
Probar un cambio de componente sin publicar en cada PR
El mayor obstáculo para la adopción de una biblioteca compartida no es escribir el componente, es la brecha entre "cambié algo" y "un equipo consumidor realmente puede probarlo." Publicar una versión temporal en cada PR supondría un costo de CI real y recurrente para un paquete que la mayoría de los PRs nunca llegan a instalar en ningún sitio, así que es opcional: un PR recibe la etiqueta needs-snapshot solo cuando un consumidor realmente necesita probarlo, lo que dispara una GitHub Action snapshot.yml que publica un paquete temporal etiquetado como @pr<número>. Un cleanup-snapshot.yml emparejado elimina la etiqueta en cuanto el PR se cierra. Los lanzamientos habituales pasan por Changesets en dos canales, @latest desde main y @beta desde una rama de staging, y el flujo de lanzamiento fusiona automáticamente su propio PR generado de "Version Packages" en cuanto la CI pasa.
Un monorepo que impone su propia dirección de dependencias
La biblioteca está dividida en capas, no en un único paquete plano: tokens, icons y styles están en la base, sin dependencias internas, core se construye sobre esas, ds se construye sobre core, y ds-patterns se construye sobre ds, core y styles. Cada capa solo puede depender hacia abajo, nunca hacia arriba, de modo que una primitiva de bajo nivel no puede terminar dependiendo en silencio de un componente de patrón de nivel superior. ds-patterns tampoco son solo primitivas genéricas: incluye widgets de dominio reales de Testlify (un CandidateCard, por ejemplo), construidos sobre la misma capa de primitivas que usa todo lo demás. Storybook, con el complemento de accesibilidad activado, documenta cada componente y se despliega públicamente, de modo que un equipo consumidor (o un revisor externo) puede comprobar los estados y los resultados de accesibilidad de un componente sin descargar el repositorio. Cada componente también lleva un archivo .a11y.spec.ts dedicado que ejecuta comprobaciones reales de axe-core en la CI, no solo un complemento de Storybook para inspección manual.
Modelo de datos y escala
- 6 paquetes publicados (
ds,-core,-icons,-patterns,-styles,-tokens), cada uno versionado de forma independiente. - 243 archivos de spec (Vitest + Vue Test Utils) que ejercitan la biblioteca directamente, no solo las apps que la consumen.
- 9 flujos de trabajo de GitHub Actions que cubren la CI, los despliegues de vista previa y producción, los despliegues de Storybook, los lanzamientos de staging y producción, los snapshots de PR y su limpieza.
- Los workspaces de pnpm más la caché de Turborepo acotan la tarea de build a los paquetes realmente afectados por un cambio dado; las pruebas se ejecutan directamente vía Vitest en todo el workspace, en lugar de a través del grafo de tareas de Turbo.
La adopción se desplegó equipo por equipo, no en un día de cambio forzado: la app de reclutador/administrador funciona hoy sobre los 6 paquetes publicados, mientras que los demás equipos de producto van incorporando componentes individuales según su propio calendario de lanzamiento, en lugar de un único cambio coordinado en las 4 aplicaciones a la vez.
Construir vs. comprar
Sin scripts de incremento de versión hechos a mano: Changesets gestiona el versionado y los changelogs de los 6 paquetes a partir de un único archivo de changeset por PR. Sin registro privado autoalojado: GitHub Packages reutiliza la misma autenticación de GitHub que ya controla el acceso al repositorio, en lugar de ejecutar y proteger un registro separado como Verdaccio.
Stack
Vue 3, TypeScript, Vite, workspaces de pnpm con Turborepo, Storybook con el complemento de accesibilidad, Changesets para el versionado, Vitest y Vue Test Utils, publicado a través de GitHub Packages.