Una pipeline che funziona non è una pipeline sana. Su AZ-400 la domanda non è “come attivo la cache” ma “dato questo sintomo - build lente, coda di job, storage che cresce, team che ignora i fallimenti - quale leva agisce sulla causa e quale sposta solo il problema”. Diamo per acquisiti i meccanismi base e concentriamoci sulle decisioni di design.

Salute della pipeline: quali segnali guidano l’azione

Azure Pipelines espone la salute nella scheda Analytics della pipeline. Il Pipeline pass rate report dà pass rate e trend, il failure trend giornaliero per stage e i top failing task con i puntatori alle run fallite. Il Pipeline duration report mostra la durata tipica di una run riuscita e i top task per durata. Entrambi si filtrano per data (default 14 giorni) e per branch; il Test failures report aggiunge i test che falliscono più spesso.

La decisione di design è quale segnale guida l’intervento. Il pass rate aggregato è reporting, non diagnosi: dice che qualcosa non va, non dove. Il valore operativo sta nella scomposizione, che indirizza il lavoro sul task responsabile della maggior parte dei fallimenti. Anche il filtro per branch non è cosmetico: un pass rate su tutti i branch mescola il ramo principale, dove il fallimento è un incidente, con i branch di feature, dove è fisiologico.

Test flaky. Un test che alterna pass e fail senza modifiche al codice distrugge la fiducia più di un fallimento vero, perché insegna al team a ignorare il rosso. Azure DevOps Services offre due strategie: la system detection, che rieffettua i test falliti e marca flaky chi passa al rerun (via task VSTest o rerun manuale dei job falliti), e la custom detection, che delega la logica a un sistema esterno comunicando l’esito via Result Meta Data - Update API.

Il trade-off sta nelle flaky test options. Per default i flaky restano nel Test Summary: la pipeline fallisce comunque, ma il tag aiuta il triage. In alternativa puoi escluderli: escono dalla percentuale di pass, finiscono in Tests not reported e il fallimento viene soppresso. È la scelta che privilegia il flusso sul rigore, e nasconde regressioni reali dietro l’etichetta di flakiness. Da sapere: cambiare sistema di detection cancella lo storico di flakiness.

Concorrenza: capacità acquistata contro tempo di ciclo

Un parallel job è la capacità di eseguire un job alla volta, ed è definito a livello di organizzazione e condiviso da tutti i progetti: non puoi dedicarne una quota a un progetto o a un agent pool. Altrettanto rilevante è quando viene consumato: solo mentre la run gira su un agent. Una run ferma in attesa di approvazione non consuma capacità; i server job e i deploy verso un deployment group nemmeno.

Ne segue che una coda lunga si diagnostica dal Pool consumption report, nella scheda Analytics dell’agent pool, che grafica job in esecuzione e in coda contro i parallel job disponibili negli ultimi 30 giorni. Backlog con running al limite significa capacità insufficiente; backlog senza saturazione significa collo di bottiglia altrove, tipicamente nelle approvazioni o nelle dipendenze fra stage.

Sul lato Microsoft-hosted il free tier per progetti privati concede un job con limite di 60 minuti per esecuzione e 1.800 minuti al mese; il tier a pagamento porta il limite a 360 minuti per job e rimuove il tetto mensile. La trappola: il primo acquisto rimuove i limiti di tempo ma non aggiunge una corsia. Sul self-hosted si paga la concorrenza, non il numero di agent. E se una run supera il tetto di tempo, la risposta non è comprare capacità ma spezzare il lavoro in più job.

Cache o artifact. I due meccanismi sembrano intercambiabili e non lo sono: usa un pipeline artifact quando un job produce file di cui un altro job ha bisogno per funzionare, usa la pipeline cache quando vuoi solo accorciare la build e l’assenza di quei file non impedisce al job di completare. La cache si configura con path e key, dove la key combina stringhe fisse e file il cui contenuto viene hashato, tipicamente un lock file. Il vincolo architetturale è che le cache sono immutabili: creata una entry per una key, non può essere aggiornata. Una key fissa produce quindi una cache che non si rinnova mai, ed è il motivo per cui va accompagnata da restoreKeys, che cerca per prefisso e restituisce l’entry più recente.

Retention: cosa puoi governare e cosa no

Su Azure DevOps Services le regole per singola pipeline non esistono più: la retention si configura solo a livello di progetto, con giorni per artifact, symbol e attachment, giorni per le run e per le run di pull request, e numero di run recenti da mantenere per pipeline.

Quest’ultima ha una semantica controintuitiva. Con Azure Repos il sistema mantiene le ultime N run per il branch di default, le ultime N per ogni branch protetto e le ultime N per la pipeline nel complesso: le run conservate sono ben più di N. Con altri repository Git o TFVC vale il semplice “ultime N per pipeline”. Una run viene poi eliminata solo se tutte le condizioni sono vere, inclusa la meno ovvia: non essere trattenuta da una release.

La leva fine è il retention lease, gestibile via Lease API e invocabile dalla pipeline con uno script usando le variabili predefinite runId e definitionId. È la risposta al requisito “le build che vanno in produzione vanno conservate più a lungo di quelle di test”: la policy di progetto resta unica, la differenziazione la introduci a runtime. Il limite che l’esame ama: in una pipeline YAML multistage non puoi configurare la retention per ambiente di deployment, mentre con le classic release la personalizzazione per stage esiste.

Eliminando una run spariscono log, artifact, symbol, binari, risultati dei test e metadati, ma la retention delle run non governa i pacchetti - Universal Packages, NuGet e npm hanno il proprio ciclo di vita. E se la compliance impone di conservare output oltre la finestra, va copiato su storage proprio con il task Copy files: tutto ciò che è pubblicato come artifact resta soggetto alla pulizia.

Su GitHub Actions la struttura è la stessa con parametri diversi: log e artifact durano 90 giorni di default, configurabili per repository, organizzazione ed enterprise, con override via retention-days. La concorrenza è invece dichiarativa: concurrency definisce un group serializzato e cancel-in-progress decide se la run entrante annulla quella attiva o si accoda.

Migrazione da classic a YAML: la traduzione è parziale

La funzione Export to YAML esiste, ma il perimetro è la vera informazione da portare all’esame: solo le pipeline classic create con il classic build designer sono esportabili, e l’opzione compare dalla vista della definizione, non da una run. Le classic release pipeline non supportano l’export: vanno ricostruite esportando i singoli task. Una migrazione va quindi pianificata su due percorsi distinti, e per la parte di release il lavoro è riscrittura, non conversione.

Anche sulla parte convertibile l’output è solo un punto di partenza. Le variabili definite nell’editor classic non arrivano nel file esportato e vanno ridichiarate. Le schedule cron cambiano semantica: lo YAML usa UTC per default, le pipeline classic il fuso orario dell’organizzazione. Non produce errori di sintassi, sposta silenziosamente gli orari di esecuzione.

Come cade all’esame

  • “Build lente, il team vuole sapere dove intervenire”Pipeline duration report e top task per durata, non ottimizzazione generica né acquisto immediato di parallel job. Per il pass rate basso, l’equivalente sono i top failing task, filtrando sul branch di default.
  • “I test falliscono a intermittenza e il team ignora i risultati”flaky test management con system detection via rerun; escluderli dal Test Summary sopprime anche fallimenti reali.
  • “Molti job in coda: serve comprare capacità?” → prima il Pool consumption report; una run in attesa di approvazione non consuma un parallel job. E il primo acquisto Microsoft-hosted rimuove i limiti di tempo, non aggiunge una corsia.
  • “Il ripristino delle dipendenze pesa a ogni run”Cache task con key sul lock file e restoreKeys come fallback; mai una key fissa da sola, perché le cache sono immutabili.
  • “Le build promosse in produzione vanno conservate più a lungo”retention lease via Lease API; in YAML multistage la retention non è configurabile per ambiente. Per la compliance pluriennale, Copy files su storage proprio.
  • “Migriamo le classic release a YAML automaticamente” → non è possibile: l’export copre le classic build; ricontrolla variabili e schedule cron, che in YAML sono in UTC.