Testlify opera come una suite di 4 applicazioni web separate. In precedenza, ogni team di prodotto sviluppava e stilizzava i componenti UI comuni in modo indipendente, causando duplicazione di lavoro di ingegneria e incoerenze visive tra i prodotti. Ho proposto l'idea e guidato il team che ha costruito una libreria di componenti Vue 3 condivisa per standardizzare lo sviluppo dell'interfaccia.
Cosa ho costruito
- Consolidamento architetturale: guidata l'iniziativa per consolidare le primitive UI comuni in una libreria centralizzata, stabilendo design token e contratti dei componenti coerenti tra i team.
- Design dei componenti stateless: mantenuti i componenti della libreria rigorosamente stateless e presentazionali, permettendo loro di integrarsi in modo pulito in una qualsiasi delle 4 applicazioni mantenendo la logica di business disaccoppiata.
- Distribuzione come pacchetti privati: pubblicata e mantenuta la libreria come pacchetti versionati su GitHub Packages, permettendo ai team di adottare correzioni di bug e nuovi componenti tramite normali aggiornamenti delle dipendenze senza duplicazione di codice.
Perché è stato importante
Centralizzare lo sviluppo UI in un pacchetto condiviso ha eliminato codice ridondante tra le 4 applicazioni, accelerato i cicli di rilascio delle funzionalità e garantito un design system coerente in tutta la suite di prodotti Testlify.
Guidata, non da solo
Ho proposto la libreria e guidato l'architettura, ma non è stata una costruzione a una persona sola: 86 commit da 7 contributori a partire da maggio 2026, con i miei commit che costituiscono circa due terzi del totale. Far adottare un design system condiviso da 4 team di prodotto richiede più che scrivere i componenti; richiede il processo di rilascio che rende l'adozione a basso rischio, che è diventato il vero problema difficile.
Testare una modifica di componente non ancora rilasciata senza pubblicare a ogni PR
Il più grande ostacolo all'adozione di una libreria condivisa non è scrivere il componente, è il divario tra "ho cambiato qualcosa" e "un team consumatore può davvero provarlo". Pubblicare una versione temporanea a ogni singola PR sarebbe un costo CI reale e ricorrente per un pacchetto che la maggior parte delle PR non installa mai da nessuna parte, quindi è opt-in: una PR riceve un'etichetta needs-snapshot solo quando un consumatore ha effettivamente bisogno di testarla, il che attiva una GitHub Action snapshot.yml che pubblica un pacchetto temporaneo taggato @pr<numero>. Un cleanup-snapshot.yml abbinato rimuove il tag alla chiusura della PR. I rilasci regolari passano attraverso Changesets su due canali, @latest da main e @beta da un branch di staging, e il workflow di rilascio unisce automaticamente la propria PR generata "Version Packages" una volta che la CI passa.
Un monorepo che impone la propria direzione delle dipendenze
La libreria è suddivisa in livelli, non un unico pacchetto piatto: tokens, icons e styles stanno alla base senza dipendenze interne, core si costruisce su questi, ds si costruisce su core, e ds-patterns si costruisce su ds, core e styles. Ogni livello può dipendere solo verso il basso, mai verso l'alto, così una primitiva di basso livello non può crescere silenziosamente una dipendenza verso un componente pattern di livello più alto. ds-patterns non è nemmeno solo un insieme di primitive generiche: include veri widget di dominio Testlify (una CandidateCard, per esempio), costruiti sullo stesso livello di primitive usato da tutto il resto. Storybook, con l'addon di accessibilità abilitato, documenta ogni componente ed è distribuito pubblicamente, così un team consumatore (o un revisore esterno) può controllare gli stati di un componente e i risultati di a11y senza dover clonare il repository. Ogni componente porta con sé anche un file .a11y.spec.ts dedicato che esegue veri controlli axe-core in CI, non solo un addon Storybook per l'ispezione manuale.
Modello dati e scala
- 6 pacchetti pubblicati (
ds,-core,-icons,-patterns,-styles,-tokens), ciascuno versionato indipendentemente. - 243 file di spec (Vitest + Vue Test Utils) che testano direttamente la libreria, non solo le applicazioni consumatrici.
- 9 workflow GitHub Actions che coprono CI, deploy di anteprima e produzione, deploy di Storybook, rilasci di staging e produzione, snapshot delle PR e pulizia degli snapshot.
- workspace pnpm più cache Turborepo che delimitano il task di build ai pacchetti effettivamente interessati da una determinata modifica; i test girano direttamente tramite Vitest sull'intero workspace invece che attraverso il grafo dei task di Turbo.
L'adozione è stata distribuita per team, non con un cutover forzato in un solo giorno: l'app recruiter/admin gira oggi su tutti i 6 pacchetti pubblicati, mentre gli altri team di prodotto adottano singoli componenti secondo il proprio calendario di rilascio invece di un unico passaggio coordinato su tutte e 4 le applicazioni contemporaneamente.
Costruire vs. comprare
Nessuno script di version bump scritto a mano: Changesets gestisce il versionamento e i changelog su tutti e 6 i pacchetti a partire da un unico file di changeset per PR. Nessun registry privato self-hosted: GitHub Packages riutilizza la stessa autenticazione GitHub che già regola l'accesso al repository, invece di gestire e proteggere un registry separato come Verdaccio.
Stack
Vue 3, TypeScript, Vite, workspace pnpm con Turborepo, Storybook con l'addon di accessibilità, Changesets per il versionamento, Vitest e Vue Test Utils, pubblicata tramite GitHub Packages.