Costruisci l'artefatto una sola volta e promuovilo invariato
Produci un singolo artefatto immutabile e versionato nella CI e distribuisci quell'esatto artefatto, riferito per digest del contenuto, attraverso ogni ambiente. Ricompilare per ogni ambiente lascia derivare versioni della toolchain e dipendenze transitive, così i bit testati non sono i bit che spedisci.
- Fai: costruisci un'immagine container una volta, fai il push in un registry e promuovila per digest da staging a produzione.
- Non fare: rieseguire
docker buildnel job di deploy di produzione dallo stesso sorgente "per sicurezza".
Tieni le definizioni delle pipeline sotto controllo di versione
Conserva la tua pipeline come codice (.github/workflows/ci.yml, .gitlab-ci.yml, Jenkinsfile) nello stesso repository dell'applicazione, così che sia revisionata, versionata e riproducibile per qualsiasi commit passato. Le pipeline configurate solo in una UI web sono invisibili, non revisionabili e impossibili da revertire insieme al codice.
- Fai: committa il file di workflow e richiedi la review per le sue modifiche.
- Non fare: modificare gli step del job direttamente nella UI del server CI dove la modifica non lascia diff.
Fallisci presto e ordina gli stage dal più economico
Esegui i controlli rapidi ed economici (lint, unit test, secret scan) prima di quelli lenti e costosi (test di integrazione, end-to-end, deploy) così che gli sviluppatori abbiano feedback in pochi secondi e un errore banale non consumi un'intera esecuzione di pipeline. Una pipeline che esegue una suite di 30 minuti prima di intercettare un errore di lint spreca tempo e denaro.
- Fai: metti format, lint e unit test nel primo stage e interrompi al fallimento.
- Non fare: schedulare per prima la suite di integrazione più lunga e scoprire un errore di sintassi dopo 25 minuti.
Non conservare mai segreti nel repository o nei log di build
Tieni tutte le credenziali in un secrets manager dedicato o nello store crittografato del provider CI e iniettale come variabili mascherate a runtime; aggiungi il secret-scanning a pre-commit e CI. Un segreto nella cronologia Git o in un log di build è esposto a chiunque abbia accesso in lettura e va ruotato.
- Fai: leggi
DATABASE_PASSWORDda un segreto iniettato e preferisci token a vita breve federati via OIDC. - Non fare: hardcodare una chiave API nel file di workflow o fare
echodi un token mentre fai debug.
Rendi i deploy reversibili prima di renderli frequenti
Adotta una strategia di deploy con rollback integrato (blue-green, canary o revert GitOps) e provala così che revertire sia routine, non un'emergenza improvvisata. Fare deploy spesso è sicuro solo quando il ripristino è rapido, motivo per cui l'MTTR conta tanto quanto la deployment frequency.
- Fai: collega il rollback automatico ai fallimenti degli smoke test e delle metriche canary.
- Non fare: distribuire direttamente al 100% del traffico senza un percorso testato verso la versione precedente.
Progetta le modifiche di database perché siano retrocompatibili
Usa il pattern expand-then-contract così che vecchia e nuova versione applicativa possano coesistere durante un rollout e così che un rollback del codice non lasci mai lo schema orfano. Le modifiche additive vanno per prime e sono sicure da revertire; quelle distruttive avvengono solo dopo che nessun codice in esecuzione referenzia la forma vecchia.
- Fai: aggiungi una colonna nullable, distribuisci codice che scrive su vecchia e nuova, fai il backfill, poi elimina la vecchia colonna in un rilascio successivo.
- Non fare: rinominare o eliminare una colonna nello stesso deploy che commuta il codice al nuovo nome.
Tratta l'infrastruttura come codice con review plan-then-apply
Definisci l'infrastruttura in IaC sotto controllo di versione e richiedi un piano revisionato (terraform plan, helm diff) prima di qualsiasi apply, così che l'effetto di una modifica sia visibile e approvato prima di toccare la produzione. Le modifiche manuali in console creano drift che rende gli ambienti non riproducibili.
- Fai: apri una PR con l'output del piano allegato e applica solo dopo l'approvazione.
- Non fare: navigare la console cloud per "sistemare al volo" una risorsa gestita dall'IaC.
Sposta i controlli di sicurezza a sinistra, nella pull request
Esegui la scansione delle dipendenze (SCA), l'analisi statica (SAST), il rilevamento dei segreti e la scansione dell'IaC su ogni pull request così che le vulnerabilità siano intercettate quando costano meno da correggere e l'autore ha ancora contesto. Trovare lo stesso problema in un audit tardivo costa molto di più e arriva senza contesto.
- Fai: fai fallire la PR quando una scansione delle dipendenze trova una CVE ad alta gravità sopra la tua soglia.
- Non fare: rimandare tutta la review di sicurezza a un gate manuale la settimana prima del rilascio.
Rendi idempotente ogni step di automazione
Scrivi script di deploy, IaC e configurazione così che rieseguirli dopo un fallimento parziale converga verso lo stato desiderato anziché duplicare risorse o dare errore. Le pipeline falliscono e riprovano; uno step non idempotente trasforma un fallimento transitorio in un ambiente corrotto.
- Fai: usa un apply dichiarativo che riconcilia verso lo stato desiderato e tollera le riesecuzioni.
- Non fare: script append-only che creano un secondo load balancer ogni volta che il job riprova.
Strumenta per l'observability, non solo per il monitoring
Emetti metriche, log e tracce strutturati (idealmente via OpenTelemetry) così da poter sia allertare su fallimenti noti sia indagare quelli imprevisti senza rilasciare nuovo codice. Le dashboard per problemi noti sono necessarie ma insufficienti quando arriva un incidente nuovo.
- Fai: propaga un trace ID tra i servizi e registra metriche RED (rate, errors, duration) per endpoint.
- Non fare: affidarti a grep su log non strutturati tra gli host durante un incidente live.
Definisci gli SLO e governa i rilasci con un error budget
Fissa Service Level Objective espliciti supportati da Service Level Indicator, e usa l'error budget risultante per decidere quando continuare a spedire funzionalità rispetto a fermarsi per il lavoro di affidabilità. Senza un numero oggettivo, l'argomento velocità-contro-stabilità lo decide chi urla più forte.
- Fai: metti in pausa i rollout rischiosi quando l'error budget a 30 giorni è esaurito.
- Non fare: rincorrere il 100% di disponibilità, impossibilmente costoso e non ciò di cui gli utenti hanno bisogno.
Usa runner di build effimeri e a privilegio minimo
Esegui i job CI su runner effimeri distrutti dopo ogni job e concedi loro solo i permessi necessari, delimitati per pipeline. I runner condivisi di lunga durata accumulano stato tra le build e diventano un bersaglio di alto valore la cui compromissione espone i segreti di ogni progetto.
- Fai: usa runner effimeri freschi e credenziali a vita breve e delimitate strettamente per job.
- Non fare: riusare un runner persistente con credenziali admin ampie in cache su disco.
Genera una SBOM e pinna le dipendenze per hash
Produci una Software Bill of Materials per ogni build e pinna le dipendenze a versioni esatte o hash del contenuto, così da poter rispondere "siamo colpiti?" in pochi minuti da una nuova CVE e così che un pacchetto manomesso o typosquattato non possa infilarsi silenziosamente. Range di versione fluttuanti e nessun inventario trasformano una vulnerabilità divulgata in un'indagine senza fine.
- Fai: blocca le dipendenze con un lockfile verificato per hash ed emetti una SBOM CycloneDX o SPDX come artefatto di build.
- Non fare: dipendere da tag
latesto range non pinnati che si risolvono diversamente a ogni build.
Tieni i feature flag a vita breve e ripuliscili
Usa i feature flag per disaccoppiare il deploy dal rilascio e per abilitare kill switch istantanei, ma rimuovi ogni flag una volta completato il suo rollout. I flag obsoleti moltiplicano i percorsi di codice, gonfiano le combinazioni di test e diventano bug latenti di cui nessuno ricorda il significato.
- Fai: traccia ogni flag con un responsabile e una data di rimozione, e cancellalo dopo il rollout completo.
- Non fare: lasciare un ramo permanente
if (flagEnabled)nel codebase un anno dopo il lancio.