Standard di codifica
Linguaggio e tipi
- TypeScript ESM,
strictattivo conexactOptionalPropertyTypesenoUncheckedIndexedAccess. anyvietato insrc/(ESLinterror). I dati esterni entrano comeunknowne si restringono con Zod.- Import con estensione
.js(requisito ESM). Le pipeline importano dal barrelsrc/core/index.ts. - Preferire funzioni e dati immutabili (
readonly,const). Evitare stato globale.
Architettura ETL
- Separare estrazione, trasformazione e caricamento. I side-effect verso sistemi esterni stanno solo in
Source/Sink. LeTransformsono funzioni pure e testabili. - Iniezione delle dipendenze. Una pipeline riceve
SourceeSinkcome parametri (vedibuildSalesDailyPipeline), così la si testa con connettori in-memory e si cambia ambiente senza toccare la logica. - Streaming di default. I connettori restituiscono
AsyncIterable: non caricare interi dataset in memoria. - Schema-contratto su ogni confine. Validare con Zod ciò che entra dalla sorgente e ciò che esce verso il sink (
validate()), così i dati malformati non si propagano.
Naming
- File:
kebab-case.ts. Tipi/classi:PascalCase. Funzioni/variabili:camelCase. Costanti:UPPER_SNAKE. - Stage con nome descrittivo e unico nella pipeline (compare in log/metriche):
"raw-contract","to-fact". - Pipeline: nome
kebab-caseche descrive il dominio e la cadenza:langfuse-sessions-daily,orders-hourly.
Errori
- Lanciare solo sottoclassi di
EtlError(ValidationError,ExtractError,LoadError,ConfigError). - Un
EtlErrorcon record problematico è instradabile al dead-letter; un errore generico aborta la run (è un bug da fixare). - Mai inghiottire errori in silenzio. O dead-letter, o fail-fast esplicito.
Logging
- Usare
ctx.logger(JSON strutturato su stderr), maiconsole.log(ESLint lo segnala). - Niente segreti nei log. Loggare conteggi e identificatori, non payload sensibili.
- Arricchire il contesto con
logger.child({ stage, ... }).
Idempotenza (regola d'oro dell'ETL)
- Un
Sink.loaddeve poter essere rieseguito senza creare duplicati: usare upsert /MERGEsu chiave naturale, oppure write-then-swap (scrivi su tabella temp, poi swap atomico). - Il caricamento incrementale legge il watermark da
ctx.params(es.since) e lo avanza solo a caricamento riuscito.

