5 app in un solo repository: il monorepo Turborepo di uno sviluppatore indie
Come organizzare più app e siti web in un unico repository con un monorepo Turborepo. Condivisione delle configurazioni comuni, build risparmiate grazie alla cache e deploy per singola app, dal punto di vista pratico.
In sintesi
Quando costruisci più app da solo, le configurazioni si disperdono. Con un monorepo Turborepo condividi eslint e tsconfig tramite packages, con la cache di turbo eviti di ricostruire le app non modificate e con --filter fai il deploy app per app.
In questa pagina
Quando costruisci app da solo, i repository si moltiplicano a vista d'occhio. Un'app, un sito, un'altra app... e ti ritrovi a copiare e incollare la configurazione di eslint cinque volte. Io ho unito tutto in un unico monorepo.
Il vero problema sono le configurazioni disperse
Con tanti repository, quello che fa più male non è il codice: sono le configurazioni.
- eslint, prettier e tsconfig copiati in ogni repository
- Cambi una regola e devi fare il giro per correggerli tutti
- UI e utility comuni troppo piccole per valere una pubblicazione su npm
Il monorepo trasforma tutto questo in condivisione da un unico posto.
Struttura di base
apps/
├── web/ # 랜딩
├── blog/ # 이 블로그
├── wave/ # 오늘의 파도
└── ...
packages/
├── eslint-config/ # 공용 eslint
└── typescript-config/ # 공용 tsconfig
apps/* sono gli artefatti da distribuire, packages/* sono configurazioni e codice condivisi tra le app. Il tsconfig.json di ogni app si limita a ereditare così.
{ "extends": "@repo/typescript-config/nextjs.json" }
Da ora in poi le regole si correggono solo in un posto: packages.
La cache di turbo è il cuore
La vera arma di Turborepo è la cache. Se gli input di un task (sorgenti e configurazione) non cambiano, il risultato precedente viene riutilizzato così com'è.
turbo run build # 전체 빌드(바뀐 것만 실제로 돎)
turbo run build --filter=blog # blog 앱만
In turbo.json basta dichiarare le dipendenze tra i task e gli output.
{
"tasks": {
"build": { "dependsOn": ["^build"], "outputs": [".next/**"] }
}
}
Dalla seconda build in poi, con "FULL TURBO", si chiude in pochi secondi.
Deploy per singola app
Anche con un unico repository, i deploy restano separati per app. Con Docker puoi usare turbo prune per ritagliare solo il sottoalbero dell'app e ottenere un'immagine leggera.
turbo prune blog --docker # blog + 내부 의존성만 남긴 out/ 생성
Anche questo blog l'ho ritagliato così e messo in Docker su un mini PC. Quella storia la racconto nel post sul self-hosting.
In sintesi
apps/*= artefatti da distribuire,packages/*= configurazioni condivise- tsconfig ed eslint gestiti in un posto solo tramite ereditarietà
- Con la cache di
turbo, le app non modificate non vengono ricostruite - Deploy per app con
--filtereprune
Se gestisci più prodotti da solo, il monorepo non è un lusso: è uno strumento di sopravvivenza.
Domande frequenti
Da quante app in su conviene un monorepo?
Già oltre le 2 app, appena nascono configurazioni comuni (eslint, tsconfig, UI), conviene. Il punto chiave è correggere la configurazione in un posto solo e vederla riflessa in tutte le app.
Le build non diventano più lente?
Al contrario, diventano più veloci. turbo mette in cache i risultati dei task i cui input non sono cambiati e ricostruisce solo le app modificate.
Articoli correlati
- 💻 Sviluppo
Se un bucket ha due proprietari, vince l'ultimo che fa apply
Ho aggiunto una regola lifecycle a S3 e terraform apply è fallito. La causa: due risorse possedevano ciascuna la configurazione lifecycle dello stesso bucket. Il lifecycle di S3 non funziona per singola regola ma per documento intero, quindi l'ultimo vincitore cancella in silenzio le regole dell'altro.
- 💻 Sviluppo
I test erano verdi, ma le notifiche non erano mai partite
Il codice che accodava i job falliva sempre, fin dal primo giorno. L'errore veniva inghiottito e i test restavano verdi grazie ai mock. La storia di come un singolo due punti ha ucciso in silenzio tre funzionalità.
- 💻 Sviluppo
È arrivata la notifica che il DB era morto. Il DB non è mai morto
In una sola giornata si sono accumulati quattro alert CRITICAL. 504, Prisma P2028, un 500 sull'admin e 'servizio DATABASE down'. Ho aperto per prime le metriche RDS: per 21 ore filate la CPU era arrivata al massimo al 19.7%, tutto tranquillo. A essere lento non era il DB, ma un andirivieni che attraversava l'Atlantico venticinque volte, e l'alert di 'down' se l'era fabbricato da solo l'health check.