
Discutiamo perché gli strumenti CI e CI sono completamente due cose diverse.
Quale problema ci si propone di risolvere con CI, da dove è nata l'idea, quali sono le ultime conferme che funziona, come capire che avete davvero una pratica, e non solo un Jenkins installato.
L'idea di fare una presentazione sulla Continuous Integration è nata un anno fa, quando stavo facendo colloqui per cercare lavoro. Ho parlato con 10-15 aziende e solo una ha potuto spiegare in modo chiaro cosa sia il CI e come hanno capito di non averlo. Gli altri, invece, parlavano di Jenkins in modo confuso 🙂 Beh, noi abbiamo Jenkins, fa le build, CI! Nella mia presentazione cercherò di spiegare cos'è realmente la Continuous Integration e perché Jenkins e strumenti simili hanno un rapporto molto debole con essa.

Quindi, cosa viene in mente quando si parla di CI? Alla maggior parte delle persone verrà in mente Jenkins, Gitlab CI, Travis, ecc.

Anche se facciamo una ricerca su Google, ci appariranno questi strumenti.

Se chiedete a qualcuno se li conosce, subito dopo aver elencato gli strumenti, vi diranno che il CI è quando nel vostro Pull Request sul commit avviene la build e l'esecuzione dei test.

La Continuous Integration non riguarda gli strumenti, non riguarda le build con i test in un ramo! La Continuous Integration è una pratica di integrazione molto frequente di nuovo codice e per applicarla non è affatto necessario costruire Jenkins, GitLab, ecc.

Prima di capire come appare un CI completo, immergiamoci prima nel contesto delle persone che l'hanno ideato e sentiamo il dolore che cercavano di risolvere.

E risolvevano il problema del lavoro di squadra!

Diamo un'occhiata agli esempi delle difficoltà che i programmatori affrontano nello sviluppo di squadra. Abbiamo un progetto, un ramo master in git e due sviluppatori.

E hanno iniziato a lavorare come sono abituati da tempo. Hanno preso un compito su Jira, hanno creato un ramo per la funzione, e scrivono codice.

Uno ha completato la funzione prima e l'ha unita al master.

L'altro ha impiegato più tempo, ha unito dopo e ha ricevuto un conflitto. Ora, invece di scrivere le funzionalità necessarie per il business, lo sviluppatore sta sprecando tempo ed energie per risolvere conflitti.

Più è complesso unire la tua funzionalità al master generale, più tempo ci mettiamo. E questo è solo un esempio piuttosto semplice. È un caso in cui ci sono solo 2 sviluppatori. E immagina se ci fossero 10, 15 o 100 persone in azienda che scrivono in un unico repository. Impazziresti a risolvere tutti questi conflitti.

C'è un caso leggermente diverso. Abbiamo un master e diversi sviluppatori che stanno facendo qualcosa.

Hanno creato un ramo ciascuno.

Uno si è fuso, tutto bene, ha consegnato il compito.

Nel frattempo, il secondo sviluppatore ha consegnato il proprio compito. Supponiamo che lo abbia inviato per la revisione. In molte aziende esiste la pratica della revisione. Da un lato, è una pratica utile e positiva, dall'altro ci rallenta in molti casi. Non approfondiamo, ma ecco un ottimo esempio di cosa può portare una storia tortuosa con la revisione. Hai inviato una richiesta di pull per la revisione. Allo sviluppatore non resta nulla da fare. Cosa inizia a fare? Inizia a prendere altri compiti.

Nel frattempo, il secondo sviluppatore ha fatto ancora qualcosa.

Il primo ha completato il terzo compito.

E dopo un certo periodo di tempo, la sua revisione è stata provata e lui cerca di fondersi. E cosa succede? Si ritrova con un enorme numero di conflitti. Perché? Perché mentre la sua richiesta di pull era in revisione, nel codice sono già cambiate molte cose.
Oltre alla questione dei conflitti, c'è la questione delle comunicazioni. Finché il tuo ramo è in revisione, mentre aspetta, mentre stai lavorando a lungo su una funzionalità, smetti di monitorare cosa sta cambiando nel codice della tua base di servizio. Forse quello che stai cercando di risolvere adesso è già stato risolto ieri e puoi riutilizzare un qualche metodo. Ma non lo vedrai, perché stai sempre lavorando con un ramo obsoleto. E questo ramo obsoleto porta sempre a dover risolvere conflitti di merge.
Quindi, se lavoriamo in squadra, ovvero non c'è una sola persona che si occupa del repository, ma ci sono 5-10 persone, più a lungo non aggiungiamo il nostro codice al master, più soffriamo perché alla fine dobbiamo unire qualcosa. E più conflitti abbiamo, e più lavoriamo con una versione vecchia, più problemi avremo.

Fare qualcosa insieme è doloroso! Ci infastidiamo sempre a vicenda.

Questo problema è stato notato oltre 20 anni fa. La prima menzione della pratica di Continuous Integration l'ho trovata nella programmazione estrema.
La programmazione estrema è il primo framework agile. La pagina è apparsa nel '96. L'idea era di utilizzare alcune pratiche di programmazione, pianificazione e altro, affinché lo sviluppo fosse il più flessibile possibile, per poter reagire più rapidamente a cambiamenti e richieste dei nostri clienti. E 24 anni fa hanno iniziato a confrontarsi con il fatto che, se si lavora a lungo e in modo isolato, si spende più tempo, perché si creano conflitti.

Ora analizzeremo l'espressione "Continuous Integration" parola per parola. Se la traduciamo letteralmente, risulta essere integrazione continua. Ma non è molto chiaro quanto sia continua, è piuttosto interrotta. Non è nemmeno chiaro quanto sia integrazione.
E quindi vi porto ora delle citazioni dalla programmazione estrema. Analizzeremo entrambe le parole separatamente.
Integrazione — Come ho già detto, miriamo a far sì che ogni ingegnere lavori con l'ultima versione del codice, e che il suo codice venga aggiunto il più spesso possibile al ramo principale, in modo che questi siano rami piccoli. Perché se sono grandi, possiamo rimanere bloccati per una settimana con conflitti di merge. Soprattutto se abbiamo un ciclo di sviluppo lungo come il waterfall, dove lo sviluppatore si allontana per un mese a lavorare su una grande funzionalità. E durante l'integrazione potrebbe rimanere bloccato a lungo.
Integrazione — è quando prendiamo il nostro ramo e lo integriamo con il master, facciamo il merge. C'è una versione estrema, in cui siamo transbase developer, dove miriamo a scrivere direttamente nel master senza rami superflui.
In sintesi, l'integrazione consiste nel prendere il proprio codice e portarlo nel master.

Cosa si intende qui con la parola «continuous», cosa si intende per continuità? La pratica implica che lo sviluppatore cerchi di integrare il proprio codice il prima possibile. Questo è il suo obiettivo nell'affrontare qualsiasi compito: far sì che il suo codice arrivi nel master il prima possibile. In un mondo ideale, gli sviluppatori farebbero questo ogni poche ore. Cioè prendi un piccolo compito, lo unisci al master. Tutto fantastico. Questo è ciò a cui aspiri. E devi farlo in modo continuo. Appena fai qualcosa, lo metti subito nel master.
E lo sviluppatore che fa qualcosa è responsabile di ciò che ha fatto affinché funzioni e non rompa nulla. Qui di solito emerge la questione dei test. Vogliamo eseguire alcuni test sul nostro commit, sul nostro merge, per assicurarci che funzioni. E qui Jenkins può davvero aiutarti.
Ma con storie come: facciamo in modo che le modifiche siano piccole, facciamo in modo che i compiti siano piccoli, facciamo in modo che facciamo un compito e subito proviamo a unirlo al master – qui nessun Jenkins può aiutare. Perché Jenkins ti aiuta esclusivamente a eseguire i test.
Puoi fare a meno di loro. Questo non ti ostacola affatto. Perché l'obiettivo della pratica è unire il codice il più frequentemente possibile, così da non perdere un'enorme quantità di tempo su eventuali conflitti futuri.
Immaginiamo di trovarci nel 2020 senza internet, per qualche motivo. E lavoriamo localmente. Non abbiamo Jenkins. Va bene. Puoi comunque prendere e fare un branch locale. In esso hai scritto del codice. Hai completato il compito in 3-4 ore. Ti sei spostato su master, hai eseguito git pull, hai unito il tuo branch. Fatto. Se lo fai spesso – congratulazioni, hai Continuous Integration!

Quali prove esistono nel mondo moderno sul fatto che valga la pena investire energie in questo? Perché in generale è difficile. Se provi a lavorare in questo modo, capirai che dovrai affrontare un certo tipo di pianificazione, dovrai dedicare più tempo alla scomposizione dei compiti. Perché se fai man…, non potrai unire rapidamente e, di conseguenza, finirai nei guai. Non avrai più pratiche.
E sarà costoso. Non sarà possibile lavorare da domani con il Continuous Integration. Dovrete abituarvi a lungo, sarà un processo lungo abituarsi a decomporre i compiti, a lungo abituarvi a rivedere le pratiche di revisione, se ne avete. Perché il nostro obiettivo è che tutto venga fuso oggi. E se la vostra revisione richiede tre giorni, allora avete un problema e il Continuous Integration non funziona.
Ma abbiamo delle prove attuali proprio ora che ci dicono che ha senso investire in questa pratica?

La prima cosa che mi è venuta in mente è il State of DevOps. È uno studio che i ragazzi conducono da 7 anni. Ora lo fanno come organizzazione indipendente, ma sotto Google.
E la loro ricerca nel 2018 ha mostrato una correlazione tra le aziende che cercano di utilizzare rami a vita breve, che si integrano rapidamente e frequentemente, e i loro indicatori di prestazioni IT migliori.
Quali sono questi indicatori? Sono 4 metriche che raccolgono da tutte le aziende nei loro questionari: frequenza di distribuzione, tempo di attesa per le modifiche, tempo di ripristino del servizio, tasso di errore delle modifiche.
E, prima di tutto, c'è questa correlazione, sappiamo che le aziende che si fondono spesso hanno metriche molto migliori. Hanno anche una suddivisione delle aziende in diverse categorie: aziende lente, che producono lentamente, performer medi, performer di alto livello e elite. L'elite sono Netflix, Amazon, che sono super veloci, fanno tutto rapidamente, bene e in modo qualitativo.

La seconda storia è successa appena un mese fa. Nel Technology Radar è apparso un articolo notevole su Gitflow. Gitflow si differenzia da tutti gli altri in quanto i suoi rami vivono a lungo. Ci sono rami di rilascio che vivono a lungo, rami di funzionalità che vivono anch'essi a lungo. Questa pratica nel Technology Radar è stata spostata in HOLD. Perché? Perché le persone si trovano a dover affrontare il dolore dell'integrazione.
Se hai un ramo che vive molto a lungo, si inceppa, scade, cominciamo a spendere più tempo per apportare una modifica.
E recentemente l'autore di Gitflow ha affermato che se aspiri a Continuous Integration, se desideri integrare il tuo lavoro il più frequentemente possibile, allora Gitflow è una cattiva idea. Ha anche aggiunto in un articolo che se hai un backend in cui puoi fare questo, allora Gitflow è superfluo per te, poiché Gitflow ti rallenterà e ti provoquerà problemi di integrazione.
Questo non significa che Gitflow sia cattivo e che non debba essere usato. È utile in altri casi. Ad esempio, quando hai bisogno di supportare più versioni di un servizio, di un'applicazione, ossia quando devi fornire supporto per un periodo prolungato di tempo.
Ma se parli con persone che supportano tali servizi, sentirai molte lamentele riguardo al fatto che questa versione era la 3.2, risalente a 4 mesi fa, e che non conteneva questa correzione e ora, per apportarla, devono fare un sacco di modifiche. E così si ritrovano di nuovo bloccati, e passano una settimana a cercare di prendere e unire qualche nuova funzionalità.
Come ha giustamente fatto notare Aleksandr Kovalev nella chat, la correlazione non è la stessa cosa della causalità. È così. Ossia, non c'è un legame diretto che se hai Continuous Integration, tutte le metriche saranno fantastiche, no. Ma c'è una correlazione positiva, che se c'è una cosa, probabilmente ci sarà anche l'altra. Non è un dato di fatto, ma è probabile. È solo una correlazione.

Sembrerebbe che stiamo già iniziando a fare qualcosa, che ci stiamo già integrando, ma come possiamo capire se abbiamo veramente Continuous Integration, se stiamo integrando abbastanza frequentemente?
Jez Humble è l'autore del Handbook, Accelerate, del sito Continuous Delivery e del libro "Continuous Delivery". Propone un test di questo tipo:
- Il codice degli ingegneri viene integrato nel master ogni giorno.
- Per ogni commit esegui i test unitari.
- La build nel master è fallita, ed è stata riparata in circa 10 minuti.
Propone di utilizzare questo test per assicurarti che la pratica sia effettivamente in atto.
L'ultima parte la trovo un po' discutibile. Cioè, se puoi risolvere il problema in 10 minuti, significa che hai Continuous Integration; suona un po' strano, secondo me, ma ha senso. Perché? Perché se fai spesso merge, significa che le modifiche sono piccole. Se una piccola modifica causa un errore nella build principale, potrai trovare l'errore rapidamente, poiché la modifica è piccola. Ecco, c'era stato un piccolo merge e sono state cambiate 20-30 righe. E, di conseguenza, puoi capire rapidamente quale fosse il problema, perché le modifiche sono minuscole, hai un'area di ricerca molto ristretta.
E anche se dopo il rilascio il production crolla, se abbiamo una pratica di Continuous Integration, è molto più facile agire, perché le modifiche sono minuscole. Sì, questo influenzerà la pianificazione. Sarà doloroso. E, probabilmente, la cosa più difficile in questa pratica è abituarsi a suddividere i compiti, cioè come fare per prendere qualcosa e realizzarlo in poche ore e, nel contempo, passare la revisione, se c'è. La revisione è un doloroso affare a parte.
I test unitari sono solo un aiuto che ti permette di capire se la tua integrazione è andata a buon fine, se nulla si è rotto. A mio avviso, questo non è un punto necessariamente obbligatorio, perché il senso della pratica non sta in questo.
Questo è un breve accenno al Continuous Integration. Questo è tutto ciò che c'è in questa pratica. Sono pronto per le domande.
In breve, voglio solo riassumere:
- Il Continuous Integration non è Jenkins, non è Gitlab.
- Non è uno strumento, è una pratica che implica che uniamo il nostro codice nel master il più spesso possibile.
- Lo facciamo per evitare un enorme dolore che si verifica con i merge in futuro, cioè proviamo una piccola sofferenza ora, per non dover affrontare una maggiore in futuro. Questo è il senso di tutto.
- C'è comunicazione attraverso il codice, ma raramente la vedo; è stata pensata anche per questo.
Domande
Cosa fare con i task non decomponibili?
Decomporli. Qual è il problema? Puoi fare un esempio di un compito che non può essere decomposto?
Ci sono compiti che non possono essere decomposti, nel senso che richiedono un'esperienza molto profonda e che possono realmente essere risolti solo dopo un mese fino a un risultato accettabile.
Se ho capito bene, c'è un compito grande e complesso il cui risultato sarà visibile solo tra un mese?
Sì, esatto. Sì, potremo valutare il risultato non prima di un mese.
Va bene. In generale, non è un problema. Perché? Perché in questo caso, quando parliamo di branch, non parliamo di un branch con una feature. Le feature possono essere grandi e complesse. Possono coinvolgere un gran numero di componenti. E, forse, non possiamo completarle tutte in un solo branch. È normale. Dobbiamo semplicemente suddividere questa storia. Se la feature non è pronta fino in fondo, non significa che alcuni pezzi del suo codice non possano essere fusi. Hai aggiunto, ad esempio, una migrazione e all'interno della feature ci sono alcuni passaggi. Hai, ad esempio, un passaggio – fare la migrazione, aggiungere un nuovo metodo. E puoi già unire queste cose quotidianamente.
Va bene. Qual è il senso di questo?
Qual è il senso di unire piccole cose quotidianamente?
Sì.
Se qualcosa si rompe, lo vedi subito. Hai un piccolo pezzo che ha rotto qualcosa, è più facile sistemarlo. Il senso è che unire un piccolo pezzo ora è molto più semplice che unire qualcosa di grande dopo alcune settimane. E il terzo senso è che altri ingegneri lavoreranno con una versione attuale del codice. Vedranno che ci sono state alcune migrazioni aggiunte e che è comparso un metodo che potrebbero anche voler utilizzare. Tutti vedranno cosa sta succedendo nel tuo codice. È per queste tre cose che viene praticato.
Grazie, domanda chiusa!
(Oleg Soroka) Posso aggiungere qualcosa? Hai detto tutto giusto, voglio solo aggiungere una frase.
Va bene.
Con l'integrazione continua, il codice viene unito al branch principale non quando la feature è completamente pronta, ma quando il build ha smesso di rompersi. E puoi tranquillamente fare commit nel master quante volte vuoi al giorno. Il secondo aspetto è che se per qualche motivo non riesci a suddividere un compito di un mese in compiti almeno da tre giorni, e io taccio su tre ore, hai un enorme problema. E il fatto che non hai integrazione continua è il minore di questi problemi. Significa che hai problemi con l'architettura e le pratiche ingegneristiche sono a zero. Perché anche se si tratta di ricerca, in ogni caso deve essere formulata sotto forma di ipotesi o cicli.
Abbiamo parlato di 4 metriche che distinguono le aziende di successo da quelle in ritardo. Prima di arrivare a queste 4 metriche, bisogna sopravvivere. Se in media un compito richiede un mese, ti suggerirei di concentrarti prima su questa metrica. Abbassala a 3 giorni. Solo dopo comincia a pensare al Continuous.
Ho capito bene che pensi che investire nelle pratiche ingegneristiche non abbia senso se ogni compito richiede un mese?
Hai Continuous Integration. E lì c'è un concetto per cui in 10 minuti puoi correggere un errore o ripristinare. Immagina di farlo. Inoltre, hai anche il continuous deployment, lo hai rilasciato in produzione e solo dopo ti sei accorto che qualcosa è andato storto. Devi ripristinarlo, ma hai già effettuato la migrazione del database. La schema del database è già alla versione successiva, e di più, anche un backup è stato eseguito, e vi sono già stati scritti dei dati.
Qual è la tua alternativa? Se ripristini il codice precedente, non può più funzionare con questo database aggiornato.
Il database si muove solo avanti, sì.
Le persone che hanno una cattiva pratica ingegneristica probabilmente non hanno nemmeno letto un grande libro su ... cosa fare con i backup? Se ti ripristini da un backup, significa che perdi i dati accumulati nel frattempo. Ad esempio, hai lavorato per tre ore con la nuova versione del database, e gli utenti si sono registrati. Torni al vecchio backup, perché con la nuova versione la schema non funziona, quindi hai perso quegli utenti. E loro sono scontenti, stanno protestando.
Per padroneggiare l'intero spettro delle pratiche che supportano l'Integrazione Continua e la Consegna Continua, non basta semplicemente imparare a scrivere .... In primo luogo, potrebbero diventare molte, e questo sarebbe impraticabile. Inoltre, ci sono molte altre pratiche, come quelle Scientifiche. C'è una pratica che GitHub ha reso popolare, un tempo. È quando esegui sia il codice vecchio che quello nuovo contemporaneamente. È quando lavori su una funzionalità incompleta, ma può restituire un valore: sia come funzione, sia come API Rest. Esegui sia il codice nuovo che quello vecchio, confrontando le differenze tra di loro. E se ci sono differenze, le registri. In questo modo sai che la tua nuova funzionalità è pronta per essere distribuita sopra la vecchia, se non ci sono state discrepanze tra questi due per un certo periodo di tempo.
Ci sono centinaia di queste pratiche. Proporrei di iniziare con lo sviluppo transbase. Non è 100 % basato sull'Integrazione Continua, ma le pratiche sono le stesse, uno senza l'altro vive male.
Hai citato lo sviluppo transbase come esempio, dove si possono osservare le pratiche, o stai suggerendo alle persone di iniziare a utilizzare lo sviluppo transbase?
Osservare, poiché non saranno in grado di usarlo. Per utilizzarle, è necessario leggere molto. E quando a una persona viene chiesto: "Cosa fare con una funzionalità che richiede un mese," significa che non ha letto riguardo allo sviluppo transbase. E non lo consiglierò ancora. Consiglierei di concentrarsi esclusivamente sul tema di come suddividere architettonicamente grandi compiti in compiti più piccoli. Questo è il vero significato della decomposizione.
La decomposizione è uno degli strumenti dell'architetto. Iniziamo sempre con un'analisi, poi la decomposizione, poi la sintesi, poi l'integrazione. In questo modo tutto si compone. E per arrivare all'Integrazione Continua dobbiamo prima crescere attraverso la decomposizione. Le domande sorgono nella prima fase, mentre noi stiamo già parlando della quarta fase, cioè più spesso facciamo integrazione, meglio è. È ancora troppo presto per farla, sarebbe meglio prima rifinire il proprio monolite.
È necessario disegnare un certo numero di frecce e quadrati in uno schema. Non puoi dire che ora mostrerò lo schema architettonico della nuova applicazione e mostrare un quadrato, all'interno del quale c'è un pulsante verde per l'applicazione. In ogni caso ci saranno più quadrati e frecce. In qualsiasi schema che ho visto, ce ne erano più di uno. E la decomposizione anche a livello di rappresentazione grafica è già in atto. Quindi i quadrati possono essere resi indipendenti. Se no, ho grandi dubbi sull'architetto.
C'è una domanda dalla chat: «Se la revisione è obbligatoria e dura a lungo, un giorno o più?».
Hai problemi con la pratica. Non dovrebbe durare una revisione un giorno o più. È la stessa storia della domanda precedente, solo un po' più delicata. Se una revisione dura un giorno, significa che probabilmente si tratta di una revisione di un cambiamento molto grande. Quindi, bisogna farne meno. Nello sviluppo di transbase, che Oleg ha raccomandato, c'è una storia che si chiama revisione continua. L'idea è che facciamo richieste di pull così piccole intenzionalmente, perché miriamo a unire costantemente e poco per volta. E quindi la richiesta di pull modifica un'astrazione o 10 righe. Grazie a ciò, la revisione ci richiede qualche minuto.
Se una revisione richiede un giorno o più, significa che qualcosa non va. Innanzitutto, potrebbe esserci qualche problema con l'architettura. Oppure si tratta di un grande pezzo di codice, ad esempio di 1.000 righe. Oppure hai un'architettura così complessa che una persona non riesce a capirla. Questo è un problema secondario, ma anche questo dovrà essere risolto. Potrebbe non essere affatto necessaria la revisione. Su questo bisogna riflettere. La revisione è quella cosa che ti sta rallentando. Porta i suoi vantaggi in generale, ma bisogna capire perché lo stai facendo. È un modo veloce per comunicare informazioni, è un modo per stabilire degli standard interni o che altro? Perché ti serve? Perché la revisione deve essere fatta o molto velocemente, o annullata del tutto. È come lo sviluppo di transbase: una storia molto bella, ma solo per ragazzi maturi.
Riguardo le 4 metriche, consiglio di toglierle comunque, per capire a cosa portano. Guardare i numeri, vedere l'immagine, quanto è tutto male.
(Dmitrij) Sono pronto a discutere di questo con te. I numeri e le metriche sono fantastici, le pratiche sono fantastiche. Ma bisogna capire se sono necessari per il business. Ci sono aziende che non hanno bisogno di una tale rapidità di cambiamento. Conosco aziende in cui non possono apportare cambiamenti ogni 15 minuti. Non perché siano cattive, ma è un ciclo di vita. E per implementare funzionalità come branches e toggle, sono necessarie conoscenze approfondite.
È complicato. Se vuoi leggere di più sulla funzionalità toggle, te la consiglio vivamente. . E c'è un ottimo articolo di Martin Fowler sulle funzionalità toggle: sui diversi tipi, sui cicli di vita, ecc. La funzionalità toggle è complessa.
E tuttavia non hai risposto alla domanda: "Jenkins è necessario o no?"
Jenkins non è necessario in nessun caso, in realtà. Se vogliamo essere seri, strumenti come Jenkins e Gitlab ti offriranno comodità. Vedrai se la build è andata a buon fine o meno. Possono aiutarti, ma non forniscono pratiche. Possono darti solo un cerchietto – Ok o non Ok. E questo solo se stai scrivendo test, perché senza test è quasi inutile. Quindi è necessario perché è più comodo, ma in generale puoi vivere anche senza di esso, non perderai molto.
Cioè, se hai pratiche, significa che non ti serve?
Esattamente. Ti consiglio il test di Jez Humble. Ho un'opinione ambivalente sull'ultimo punto. Ma in generale, se hai tre cose: ti unisci costantemente, esegui test sui commit nel ramo master, ripari rapidamente la build nel master, allora forse non ti serve altro.
Mentre aspettiamo domande dai partecipanti, ho una domanda. Abbiamo parlato di codice di prodotto. Hai mai utilizzato per il codice infrastrutturale? È lo stesso tipo di codice, segue gli stessi principi e ha lo stesso ciclo di vita, oppure ci sono altri cicli di vita e principi? Di solito, quando si parla di Continuous Integration e Development, si dimentica che c'è anche il codice infrastrutturale. E ultimamente ce n'è sempre di più. Dovremmo applicare tutte queste regole anche lì?
Non solo dovremmo, sarebbe fantastico, perché in questo modo si semplificherebbe sicuramente la vita. Non appena lavoriamo con codice, non con script bash, ma abbiamo codice normale.
Aspetta, aspetta, uno script bash è comunque codice. Non toccare il mio vecchio amore.
Va bene, non calpesterò i tuoi ricordi. Ho un'avversione personale nei confronti di bash. Si rompe in modo brutto e spaventoso tutto il tempo. E si rompe spesso in modo imprevedibile, quindi non lo gradisco. Ma va bene, supponiamo che tu abbia codice in bash. Forse davvero non capisco e lì ci sono framework normali per il testing. Non sono affatto aggiornato. E otteniamo gli stessi vantaggi.
Appena iniziamo a lavorare con l'infrastruttura come se fosse codice, ci troviamo di fronte agli stessi problemi degli sviluppatori. Qualche mese fa, mi sono trovato in una situazione in cui un collega mi ha inviato una pull request di 1.000 righe in bash. E tu rimani bloccato nella revisione per 4 ore. I problemi sono gli stessi. È ancora codice. E ancora collaborazione. Rimaniamo bloccati con la pull request e ci impantaniamo con la risoluzione degli stessi conflitti di merge dello stesso bash, per esempio.
Attualmente sto seguendo con molta attenzione questa cosa sul programmazione bella dell'infrastruttura. Ho appena introdotto Pulumi nell'infrastruttura. È puro programming. È ancora più carino, perché ho tutte le possibilità del linguaggio di programmazione; quindi ho creato dei toggle carini con gli if in un attimo e tutto va bene. Cioè, la mia modifica è già nel master. È già visibile a tutti. Altri ingegneri ne sono a conoscenza. Ha già avuto un certo impatto. Ma si è attivata non per tutte le infrastrutture. Si è attivata per i miei ambienti di test, per esempio. Quindi, rispondendo di nuovo alla tua domanda, è necessario. Ci semplifica la vita come ingegneri che lavorano con il codice.
Se qualcuno ha altre domande?
Ho una domanda. Voglio continuare la discussione con Oleg. In generale, penso che tu abbia ragione: se un compito richiede un mese, hai un problema di architettura, un problema di analisi, scomposizione, pianificazione, ecc. Ma ho la sensazione che se inizi a vivere secondo il Continuous Integration, inizierai a risolvere i problemi di pianificazione, perché non puoi sfuggire a questa realtà.
(Oleg) Sì, è tutto vero. In termini di lavoro, questa pratica è paragonabile a qualsiasi altra pratica seria che cambia la cultura. La cosa più difficile nel superare questo è l'abitudine, soprattutto le cattive abitudini. E se per implementare questa pratica è necessaria una seria modifica delle abitudini delle persone intorno a te: sviluppatori, management, production manager, ti aspettano delle sorprese.
Quali sorprese possono esserci? Diciamo che hai deciso di effettuare integrazioni più frequentemente. E con le integrazioni hai legato altre cose, ad esempio, artefatti. E nella tua azienda, ad esempio, c'è una politica secondo cui ogni artefatto deve essere in qualche modo registrato in un certo sistema di archiviazione degli artefatti. Questo richiede un certo tempo. È necessario che qualcuno segni che, come release manager, ha verificato che questo artefatto è pronto per essere rilasciato in produzione. Se questo richiede 5-10-15 minuti, ma tu effettui un rilascio una volta alla settimana, dedicare mezz'ora a questo ogni settimana non è un grande prezzo.
Se fai Continuous Integration 10 volte al giorno, devi moltiplicare 10 per 30 minuti. E questo supera la quantità di tempo lavorativo di quel release manager. Si stanca semplicemente di farlo. Ci sono costi costanti per alcune pratiche. E basta.
E devi o annullare questa regola, in modo da non dover più occuparti di queste cose, cioè non assegni manualmente il grado di conformità di qualcosa a qualcosa. Ti affidi totalmente a una serie di test automatizzati di prontezza.
E se hai bisogno di un'approvazione da qualcuno affinché il principale firmi, e non entri in produzione senza che Vasya abbia detto che va bene, ecc. – tutta questa burocrazia ostacola le pratiche. Perché se ci sono attività collegate come un onere, tutto aumenta di 100 volte. Pertanto, il cambiamento sarà spesso percepito con meno entusiasmo. Perché è difficile correggere le abitudini delle persone.
Quando una persona svolge un lavoro abituale, lo fa praticamente senza pensarci. Il suo carico cognitivo è pari a zero. Esegue semplicemente il compito, la sua mente ha già un checklist, lo ha fatto mille volte. E non appena arrivi e gli dici: “Annulliamo questa pratica e da lunedì ne implementiamo una nuova”, per lui diventa un notevole carico cognitivo. E ciò si verifica immediatamente per tutti.
Pertanto, la cosa più semplice, anche se non tutti possono permettersi questo lusso, è proprio ciò che faccio sempre. Quando inizia un nuovo progetto, di solito vengono subito inglobate tutte le pratiche non testate. Finché il progetto è giovane, non rischiamo molto. Non c'è ancora produzione, non c'è nulla da distruggere. Quindi possiamo utilizzarlo come allenamento. Questo approccio funziona. Ma non tutte le aziende hanno la possibilità di avviare progetti come questi frequentemente. Anche se è un po' strano, perché ora si parla di trasformazione digitale, tutti devono avviare esperimenti per stare al passo con i concorrenti.
Qui ti scontri con il fatto che devi avere prima una comprensione di cosa devi fare. Il mondo non è perfetto, né la produzione è perfetta.
Sì, queste cose sono collegate.
Anche le aziende non hanno sempre chiaro che devono andare in una certa direzione.
Ci sono situazioni in cui nessun cambiamento è possibile. Questa è una situazione in cui il team è sotto una pressione maggiore. Il team è già abbastanza provato. Non ha tempo di riserva per esperimenti. Lavorano tutto il giorno per sviluppare funzionalità. E la direzione richiede sempre più funzionalità. In una tale situazione, nessun cambiamento è possibile. Allo staff può solo essere detto che domani faremo come ieri, dobbiamo solo fare qualche funzionalità in più. Nessuna transizione a pratiche diverse è possibile in questo senso. Questa è una situazione classica in cui non c'è tempo per affilare l'ascia, bisogna abbattere gli alberi, quindi si abbattono con un'ascia smussata. Non ci sono consigli facili qui.
(Dmitrij) Leggerò un chiarimento dalla chat: «Ma è necessario avere una vasta copertura di test a diversi livelli. Quanto tempo è dedicato ai test? È un po' costoso, richiede molto tempo».
(Oleg) Questo è un malinteso classico. Ci devono essere abbastanza test affinché voi stessi siate sicuri. L'Integrazione Continua non è una cosa in cui inizi prima con il 100% di test e solo poi inizi a praticarla. L'Integrazione Continua riduce il carico cognitivo su di voi, dato che ogni cambiamento che vedete è così ovvio che capite se romperà qualcosa o meno, anche senza test. Potete testarlo mentalmente rapidamente, perché sono piccole modifiche. Anche se avete solo tester manuali, è più semplice per loro. Avete rilasciato e detto:
Qui hai, ovviamente, esagerato.
(Dmitry) Qui non sono d'accordo. Esiste una pratica - lo sviluppo tramite test - che vi salverà proprio da questo.
(Oleg) Ecco, non ci ero ancora arrivato. La prima illusione è che bisogna scrivere esattamente il 100% dei test o che non si debba affatto praticare l'Integrazione Continua. Non è vero. Sono due pratiche parallele. E non dipendono direttamente l'una dall'altra. La vostra copertura dei test deve essere ottimale. Ottimale significa che voi stessi siete sicuri che la qualità del master, rimasta dopo il commit, vi permetta di premere il pulsante "Distribuisci" con sicurezza venerdì sera, mentre siete ubriachi. Come ci riuscite? Attraverso revisioni, copertura e un buon monitoraggio.
Un buon monitoraggio è indistinguibile dai test. Se eseguite i test una sola volta su pre-produzione, controllano solo una volta tutti i vostri scenari utente e poi basta. Se li eseguite in un ciclo infinito, allora è il vostro sistema di monitoraggio esteso che testando tutto in continuazione - è caduto o non è caduto. In questo caso, la differenza sta solo nell'unicità o molteplicità. Un insieme molto buono di test, ...lanciato indefinitamente, è monitoraggio. E un corretto monitoraggio deve essere proprio così.
E quindi, come raggiungerete esattamente lo stato in cui venerdì sera vi distribuite e andate a casa, è un'altra questione. Forse siete solo un temerario.
Torniamo un po' indietro al Continuous Integration. Siamo scivolati in un'altra pratica complessa.
E la seconda illusione è che l'MVP, si dice, debba essere fatto rapidamente, quindi i test non servono affatto. Non è del tutto così. La questione è che quando scrivi una user story in un MVP, puoi svilupparla o senza scrupoli, cioè hai sentito che c'è una certa user story e subito inizi a codificare, oppure lavorare secondo TDD. E con TDD, come dimostra la pratica, non ci si impiega di più, cioè i test sono un effetto collaterale. La pratica del TDD non riguarda solo il testare. Nonostante si chiami Test Driven Development, qui si parla in realtà di architettura. È un approccio su come scrivere esattamente ciò di cui hai bisogno e non scrivere ciò che non serve. Questa pratica è focalizzata sulla prossima iterazione del tuo sviluppo in termini di creazione dell'architettura dell'applicazione.
Perciò non è così semplice liberarsi da queste illusioni. MVP e test non si escludono a vicenda. Anzi, è piuttosto l'opposto: se sviluppi l'MVP seguendo la pratica TDD, lo farai meglio e più velocemente rispetto a farlo senza alcuna pratica e senza una logica.
È un pensiero molto non ovvio e complesso. Quando senti dire che adesso scriverò anche i test e nel frattempo farò qualcosa di più velocemente, suona assolutamente inadeguato.
(Dmitrij) Qui molte persone, quando parlano di MVP, si è pigri a scrivere qualcosa di normale. E queste sono comunque due cose diverse. Non bisogna trasformare l'MVP in una cosa brutta che non funziona.
Sì, hai ragione.
E poi, all'improvviso, l'MVP in produzione.
Per sempre.
E TDD suona molto insolito quando senti che scrivi test e sembri fare più lavoro. Questo suona molto strano, ma in realtà ci si riesce più velocemente e meglio. Quando scrivi un test, già nella tua testa stai pensando molto a quale codice e come sarà chiamato, e anche quale comportamento ci aspettiamo da esso. Non stai solo dicendo che ho scritto una certa funzione e che fa qualcosa. Prima hai pensato che avesse determinate condizioni, che sarebbe stata chiamata in un certo modo. Copri questo con i test e da questo capisci come appariranno le interfacce all'interno del tuo codice. Questo influisce notevolmente sull'architettura. Il tuo codice diventa automaticamente più modulare, perché prima cerchi di capire come lo testerai e solo dopo lo scrivi.
Con TDD è successo che a un certo punto ho assunto un mentore per Ruby, quando ero ancora un programmatore Ruby. E lui dice: «Facciamo che tu farai TDD». E io penso: «Cavolo, devo scrivere ancora qualcosa». Così ci siamo messi d'accordo che avrei scritto tutto il codice lavorativo in Python seguendo il TDD per due settimane. Dopo due settimane ho capito che non volevo tornare indietro. Provando ad applicarlo ovunque per due settimane, ti rendi conto di quanto sia diventato più semplice anche solo pensare. Ma non è ovvio, quindi consiglio a tutti che, se avete l'impressione che il TDD sia difficile, lungo e superfluo, provate a seguirlo per sole due settimane. A me sono bastate due settimane per rendermene conto.
(Dmitrij) Possiamo approfondire questo concetto dal punto di vista dell'utilizzo dell'infrastruttura. Prima di avviare qualcosa di nuovo, eseguiamo il monitoraggio, e poi lanciamo. In questo caso il monitoraggio diventa un vero e proprio test. E c'è uno sviluppo attraverso il monitoraggio. Ma quasi tutti dicono che è lungo, che è noioso, ho fatto una bozza temporanea. Se abbiamo fatto un buon monitoraggio, comprendiamo lo stato del sistema CI. E nel sistema CI ci sono molti monitoraggi. Comprendiamo lo stato del sistema, capiamo cosa accade al suo interno. E durante lo sviluppo ci assicuriamo di portare il sistema allo stato desiderato.
Queste pratiche sono conosciute da tempo. Ne parlavamo circa 4 anni fa. Ma in 4 anni praticamente nulla è cambiato.
Ma su questa nota propongo di concludere la discussione ufficiale.
Video (inserito come elemento multimediale, ma per qualche motivo non funziona):
Fonte: habr.com
