Per anni il momento più delicato del lavoro di uno sviluppatore è stato la messa in produzione. Si lavorava con checklist cartacee, si aspettava la finestra di rilascio del venerdì pomeriggio e qualcuno, alla fine, entrava sul server di produzione e faceva tutto a mano. Un comando dimenticato, una variabile d’ambiente sbagliata, una procedura eseguita in un ordine diverso dal solito, ed era un incidente.
La pipeline CI/CD è la risposta a questo problema: trasforma la sequenza di passi manuali in un percorso automatico e ripetibile, che ogni modifica al codice deve attraversare prima di arrivare agli utenti. In questa guida ti spiego come funziona davvero, dalla prima riga di codice alla produzione, con un occhio a quello che succede “sotto il cofano”. Vale anche quando il codice lo scrive un assistente AI come OpenAI Codex: è la pipeline a garantirne la qualità prima che arrivi in produzione.
Di cosa parleremo..
Cos’è una pipeline CI/CD, in due parole
Una pipeline CI/CD è una serie di stadi automatici che una modifica al codice deve superare prima di essere distribuita. Ogni stadio è un gate: se uno fallisce, la modifica si ferma lì. Se li supera tutti, alla fine hai un artefatto verificato, pronto per gli utenti.
L’obiettivo è semplice da dire e difficile da fare: portare le modifiche dalla macchina dello sviluppatore alla produzione in modo rapido e sicuro. La pipeline non è un singolo strumento, ma un insieme di sistemi che lavorano insieme: il sistema di versionamento che la attiva, gli strumenti che compilano e testano, il registro che custodisce gli artefatti, i sistemi che li distribuiscono.
CI, CD e CD: tre pratiche da non confondere
CI/CD si dice tutto attaccato, ma sono tre pratiche distinte, che risolvono problemi diversi. Capire la differenza aiuta a capire perché le pipeline sono fatte come sono.
Continuous Integration (CI)
È la pratica di integrare spesso le modifiche su un ramo condiviso, anche più volte al giorno. Ogni integrazione attiva una build e una suite di test automatici: se qualcosa si rompe, il team lo scopre subito, non dopo settimane di lavoro in isolamento.
Prima della CI gli sviluppatori lavoravano su rami separati per giorni, e quando arrivava il momento di unire tutto le differenze erano così grandi da generare conflitti enormi: il famoso merge hell. La CI elimina questa fase dolorosa: merge piccoli e frequenti significano conflitti piccoli, e i controlli automatici intercettano il codice rotto prima che finisca nelle mani di qualcun altro.
Continuous Delivery (CD)
La Continuous Delivery fa un passo in più: garantisce che ogni modifica che supera la pipeline sia in uno stato rilasciabile. Non “prima o poi rilasciabile”: rilasciabile adesso.
Una suite di test che passa non basta. Il codice deve essere compilato in un artefatto distribuibile, testato in un ambiente simile alla produzione e deve superare controlli di qualità e sicurezza. La parola chiave qui è delivery, non deployment: la pipeline porta la modifica sulla porta della produzione, ma è ancora una persona a decidere quando aprirla. Alcuni team hanno bisogno di questo passaggio finale per motivi di compliance, gestione del cambiamento o coordinamento con il business.
Continuous Deployment
Il Continuous Deployment elimina anche l’ultimo passaggio manuale: ogni modifica che supera la pipeline finisce in produzione da sola, senza approvazione umana. È il punto estremo dello spettro, e non tutti i team ci arrivano, né tutti ne hanno bisogno.
Servono tre prerequisiti: test molto completi, meccanismi di rollback veloci e affidabili, e un monitoraggio che intercetti i problemi subito dopo il rilascio. I team che operano a questo livello distribuiscono decine o centinaia di volte al giorno: la pipeline diventa l’unico meccanismo di controllo.

TIP: la differenza tra Delivery e Deployment è un solo passaggio. La Delivery tiene una persona nel giro prima della produzione, il Deployment automatizza anche quella decisione. Entrambi gli approcci sono validi: dipende dalla fiducia che hai nella tua pipeline.
L’anatomia di una pipeline: il viaggio del codice
Qualunque sia lo strumento (GitHub Actions, GitLab CI, Jenkins), la struttura di base è sempre la stessa. I nomi degli stadi cambiano, la sequenza no. Ecco il percorso completo di una modifica.

Il commit che accende tutto
Tutto parte da un commit. Quando fai push su un ramo, un webhook scatta e la pipeline parte da sola, senza trigger manuali. Il push è il segnale che qualcosa è cambiato e va verificato.
Git è lo standard universale, ma la strategia di branching che usi condiziona il comportamento della pipeline:
- Trunk-based development: tutti fanno commit direttamente sul ramo principale, anche più volte al giorno. La pipeline gira a ogni push, i problemi emergono subito, ma servono test molto solidi e feature flag per nascondere il lavoro incompleto.
- Feature branch: ognuno lavora sul proprio ramo e fa merge quando è pronto. La pipeline gira prima sul ramo e poi di nuovo dopo il merge. Più sicuro, ma rami longevi ritardano l’integrazione.
- GitFlow: rami dedicati per feature, release e hotfix, ognuno con comportamenti di pipeline diversi. Struttura forte, ma strada più lenta verso la produzione.
Build: dal codice all’artefatto
Lo stadio di build trasforma il sorgente in qualcosa di distribuibile: risolve le dipendenze, compila il codice e produce un artefatto. Può essere un’immagine Docker, un file JAR o un binario compilato: dipende dallo stack, ma il principio non cambia.
L’artefatto è il risultato immutabile della build, e segue una regola d’oro: build una volta, distribuisci ovunque. Lo stesso artefatto attraversa tutti gli ambienti senza essere ricompilato. Ricostruire introduce derive: ciò che hai testato non è più ciò che distribuisci. L’artefatto che supera i test in CI deve essere esattamente quello che gira in produzione.
Anche la velocità conta: una build lenta è una build che gli sviluppatori iniziano a evitare. Due tecniche per mantenerla rapida sono il dependency caching, che salva i pacchetti risolti tra un’esecuzione e l’altra (le dipendenze cambiano molto meno spesso del codice), e il Docker layer caching, che riusa i layer dell’immagine rimasti invariati. Il trucco è ordinare il Dockerfile dal contenuto che cambia meno a quello che cambia di più.
Test: la rete di sicurezza
Lo stadio di test verifica che l’artefatto funzioni davvero, prima che il codice raggiunga gli utenti. L’obiettivo non è eseguire tutti i test possibili, ma i test giusti nell’ordine giusto: i problemi ovvi devono fallire in fretta, i controlli costosi arrivano dopo.
Il modello di riferimento è la test pyramid:
- Unit test alla base: veloci, isolati, numerosissimi. Una suite di mille unit test si completa in meno di un minuto. Intercettano errori di logica a livello di singola funzione e dovrebbero costituire la maggior parte della copertura.
- Integration test al centro: verificano come i componenti collaborano tra loro, con i servizi e i database, per esempio attraverso API o gRPC. Più lenti e costosi da mantenere, ma trovano problemi che gli unit test non vedono.
- End-to-end test in cima: simulano il comportamento reale dell’utente sull’intero sistema. Da usare con parsimonia, solo sui percorsi più critici.

Il feedback rapido non è un optional. Una pipeline che impiega 45 minuti a segnalare un errore è una pipeline di cui gli sviluppatori smettono di fidarsi: fanno push, cambiano contesto e quando arriva il verdetto non hanno più gli elementi per correggere. I team seri puntano a una finestra di feedback di 10 minuti o meno per la suite principale.
Due tecniche per tenere sotto controllo i tempi man mano che il codice cresce sono i parallel jobs, che dividono la suite su più runner che lavorano contemporaneamente, e il test sharding, che partiziona la suite così ogni runner copre un sottoinsieme diverso. I fallimenti dei test sono cancelli voluti: servono a fermare le modifiche cattive. Una pipeline che non fallisce mai non è sana: significa solo che non sta intercettando nulla.
TIP: la piramide dei test è una questione di costi. Migliaia di unit test veloci costano poco e girano in continuazione; pochi test end-to-end costano molto e girano di rado. Se la tua suite è tutta end-to-end, aspettati pipeline lente e fragili.
Registry: l’archivio degli artefatti
Una volta testato, l’artefatto va salvato e versionato prima di poter essere distribuito. Finisce in un registry, che è la fonte di verità centrale da cui ogni ambiente lo prende. I registri più usati sono Docker Hub, Amazon ECR, Google Artifact Registry e JFrog Artifactory.
Due pratiche di versioning contano davvero:
- Semantic versioning: numeri di versione maggiori, minori e patch danno a ogni artefatto un identificatore significativo. A colpo d’occhio capisci che tipo di cambiamento stai distribuendo.
- Tag immutabili: un tag deve puntare sempre allo stesso artefatto. In produzione il tag
latestnon si usa mai: è un puntatore mobile che si sposta a ogni push, e un deploy pinnato a latest di sei mesi fa scaricherebbe oggi un’immagine completamente diversa. Si punta a un tag di versione specifico o a un digest del contenuto.
TIP: latest in produzione è una mina. “Sulla mia macchina funziona” è già un problema; “in produzione gira latest” è un incidente in attesa di succedere. Il tag latest è comodo per lo sviluppo locale, non per i rilasci.
Deploy: l’ultimo miglio
Lo stadio di deploy prende l’artefatto verificato e lo rende disponibile agli utenti. A questo punto l’artefatto è già stato costruito, testato e salvato nel registro: il deploy lo rilascia nell’ambiente di destinazione.
Questa fase spesso include approval gate, policy di deployment e attività specifiche dell’infrastruttura. Alcuni team distribuiscono in automatico, altri richiedono un’approvazione manuale prima di toccare la produzione. E il deploy può comportare lavoro extra oltre all’avvio della nuova versione dell’applicazione: le migrazioni del database vanno eseguite prima, le modifiche di configurazione applicate, le risorse infrastrutturali aggiornate.
I runner: chi esegue davvero il lavoro
La pipeline definisce il lavoro, ma sono i runner a eseguirlo: l’ambiente di calcolo dove girano i job. I runner di GitHub Actions, i runner di GitLab e gli agenti di Jenkins fanno tutti questo mestiere. Quando uno stadio viene attivato, la piattaforma assegna i job ai runner disponibili, ognuno in un ambiente isolato.
Esistono due forme di runner:
- Runner gestiti dalla piattaforma: li fornisce il provider di CI/CD. Definisci il job e la piattaforma gestisce l’infrastruttura. Meno controllo sull’ambiente, ma zero manutenzione.
- Runner self-hosted: girano su macchine tue. Servono quando hai bisogno di hardware specifico, accesso alla rete interna o più controllo sull’ambiente di build. Il prezzo è la manutenzione e la gestione della capacità.
Ogni build, ogni test, ogni deploy gira su un runner da qualche parte. Runner lenti o sovraccarichi sono una causa comune di pipeline lente, anche quando tutto il resto funziona.
Dopo il deploy: il lavoro non è finito
Un deploy riuscito può essere comunque una release fallita. La pipeline non finisce quando la nuova versione è in produzione: servono health check e smoke test per verificare che l’applicazione sia viva e risponda, e bisogna guardare i segnali di latenza e di errore, le metriche di business e gli SLO, gli obiettivi di livello di servizio che ti dicono se il servizio sta davvero funzionando come promesso.
Per questo i meccanismi di rollback non sono un ripensamento: saper tornare alla versione precedente in fretta è ciò che trasforma un incidente in un contrattempo. Una release che degrada le performance ma supera tutti i test automatici si scopre solo guardando i segnali post-rilascio, con traffico reale.
Oltre la pipeline: verso GitOps
Quando le pipeline maturano, il passo successivo è il GitOps: l’infrastruttura stessa viene descritta come codice, versionata in Git, e un operatore automatico riconcilia continuamente lo stato reale del sistema con lo stato desiderato nel repository. Se qualcosa si allontana dalla configurazione dichiarata, il sistema lo riporta in riga da solo.
È l’evoluzione naturale del discorso: se il codice applicativo viaggia in pipeline, perché non dovrebbe farlo anche l’infrastruttura su cui gira? La deriva di produzione, quel lento allontanamento tra ciò che pensi di avere e ciò che hai davvero, è il problema architetturale che GitOps è nato per risolvere.
FAQ
Qual è la differenza tra Continuous Delivery e Continuous Deployment?
Entrambe partono da una CI funzionante. La Continuous Delivery lascia una decisione umana prima della produzione: la modifica è pronta, ma una persona approva il rilascio. Il Continuous Deployment automatizza anche quel passaggio: ogni modifica che supera la pipeline finisce in produzione da sola. La differenza è un solo step, l’approvazione finale, ma cambia tutto il livello di fiducia richiesto alla pipeline.
Perché non si deve usare il tag latest in produzione?
Perché latest è un puntatore mobile: a ogni push punta a un artefatto diverso. Un deploy che mesi fa pinnava latest oggi scaricherebbe un’immagine completamente diversa da quella testata. I tag di produzione devono essere immutabili, legati a una versione semantica specifica o a un digest del contenuto.
Cosa succede se un test fallisce in una pipeline?
La modifica si ferma: quello stadio è un cancello e un fallimento blocca il passaggio allo stadio successivo. Il commit non viene integrato, o non viene rilasciato, il team viene avvisato e qualcuno interviene. I test falliti sono una funzione di sicurezza, non un fastidio: una pipeline che non fallisce mai è solo una pipeline che non sta controllando nulla.
Come si velocizza una pipeline lenta?
Tre leve: caching delle dipendenze e dei layer Docker per la build, parallel jobs e test sharding per i test, e una piramide dei test equilibrata, con tanti test veloci e pochi test lenti. L’obiettivo è una finestra di feedback sotto i 10 minuti per la suite principale: oltre, gli sviluppatori perdono il contesto e la fiducia.
La CI/CD serve anche a un piccolo team o a un progetto personale?
Sì, in proporzione. Anche da soli, avere build e test automatici a ogni push protegge da regressioni che scopriresti solo in produzione. I servizi gestiti come GitHub Actions eliminano la manutenzione dei runner, e si può partire da una pipeline minimale per poi aggiungere stadi man mano che servono.


