Nella nostra serie in corso DevOps for the Win, abbiamo documentato il nostro percorso di trasformazione DevOps. Nel Capitolo 1, abbiamo introdotto il Triangolo DevOps: Strumenti, Architettura e Persone nello Sviluppo di Prodotti, in cui abbiamo discusso gli elementi fondamentali che guidano il successo del DevOps. Nel Capitolo 2, abbiamo esplorato I primi passi nella nostra trasformazione DevOps, condividendo come abbiamo gettato le basi per cambiamenti significativi.
Ora, nel Capitolo 3, affrontiamo la prossima grande sfida che abbiamo incontrato: la progettazione orientata alla testabilità. Dopo aver migliorato la struttura del nostro team, perfezionato la definizione di completato e implementato l'analisi automatizzata del codice, ci siamo resi conto che il collo di bottiglia principale nella nostra pipeline si era spostato sulla progettazione del software, in particolare sulla testabilità. Il lavoro si accumulava nella fase di test, impedendoci di raggiungere cicli di rilascio più rapidi. Questo capitolo approfondisce il modo in cui abbiamo superato questa sfida e migliorato la nostra capacità di rilasciare software di alta qualità in modo efficiente.
Il collo di bottiglia dei test
Il motivo principale di questo ritardo era che i test erano prevalentemente manuali, incentrati sui test di interfaccia utente (UI) e sui flussi di lavoro degli utenti. Poiché l'interfaccia utente era solitamente uno degli ultimi elementi a essere completati, l'automazione era quasi del tutto assente e il test manuale diventava l'unico approccio praticabile. I casi di test UI automatizzati (ad esempio, i test Selenium) venivano solitamente scritti dopo il completamento dei test manuali, principalmente per scopi di regressione.
Questa dipendenza dai test manuali ha comportato:
- Ritardi significativi nei rilasci del software.
- La necessità di distribuzioni multiple per consentire test paralleli da parte di più membri del team QA.
Ciò ha evidenziato l'importanza di rendere il codice più facilmente testabile per ottimizzare il flusso di valore. Tuttavia, raggiungere questo obiettivo si è rivelato più difficile del previsto.
Dare priorità ai test API e ai test unitari
Per affrontare questa sfida, ci siamo concentrati su due aree principali:
- Test unitari - Garantire che i singoli componenti potessero essere testati in isolamento.
- Test API - Ridurre la dipendenza dai test basati sull'interfaccia utente e sui database.
Test API: spostamento a sinistra per un feedback più rapido
Abbiamo ridefinito la nostra strategia di test API con questi miglioramenti chiave:
- API ben definite - Le API sono state progettate per essere semplici, ben documentate e disponibili sin dalle prime fasi dello sviluppo.
- Evitare il database come punto di integrazione - Fare affidamento sui database per l'integrazione creava dipendenze che rallentavano i test. Al contrario, ci siamo orientati verso l'integrazione basata su API utilizzando REST su HTTP e GraphQL. Ciò ha ridotto al minimo il tempo di configurazione del database e migliorato l'automazione dei test.
Questo cambiamento ha ridotto significativamente i ritardi, consentendo un'automazione più rapida e test nelle fasi iniziali, dimostrando che concentrarsi su progettazioni basate prima sulle API ha migliorato sia la testabilità che l'efficienza.
Test unitari: un cambiamento di mentalità
Inizialmente, i nostri sforzi di unit testing hanno portato a rapidi aumenti della copertura dei test, ma abbiamo identificato rapidamente alcuni problemi:
- Alcuni test erano superficiali, scritti solo per raggiungere gli obiettivi di copertura del codice.
- Mancavano di una valida verifica della funzionalità, dei casi limite e degli scenari di errore.
Per contrastare questo fenomeno, abbiamo insistito su:
- La formazione degli sviluppatori sulla scrittura di test utili.
- Il refactoring del codice per migliorarne la testabilità.
Le sfide nella scrittura di codice testabile
Il principale ostacolo a un unit testing efficace era la mancanza di testabilità nel codice stesso. I problemi chiave includevano:
- Funzioni grandi e monolitiche.
- Componenti fortemente accoppiati.
- Scarsa separazione delle responsabilità.
- Astrazione insufficiente.
Questi problemi rendevano difficile isolare e testare efficacemente le singole unità. Per risolvere questo problema, abbiamo:
- Linee guida fornite: Abbiamo condiviso le migliori pratiche su:
- Selezionato le funzioni per l'unit testing.
- Il refactoring del codice per migliorarne la testabilità.
- Progettato e utilizzato mock per facilitare i test.
- Focus sui miglioramenti incrementali: Gli sviluppatori sono stati incoraggiati a fare piccoli e significativi cambiamenti per migliorare la testabilità nel tempo.
Progressi lenti ma costanti
Nonostante questi sforzi, i progressi sono stati lenti, specialmente per i prodotti legacy. L'architettura esistente ha limitato il numero di unit test che potevamo scrivere. Tuttavia, queste azioni non miravano solo a migliorare il codice attuale, ma rappresentavano un investimento per il futuro:
- Il miglioramento delle competenze degli sviluppatori nella progettazione di codice testabile ha garantito che i progetti futuri non avrebbero sofferto degli stessi problemi.
- I miglioramenti incrementali hanno evitato interruzioni, aumentando al contempo in modo costante la copertura dell'automazione dei test.
- Un cambiamento di mentalità ha aiutato i team a considerare la testabilità non come un peso, ma come una necessità per il successo sostenibile di DevOps.
Per i team e le organizzazioni che intraprendono una trasformazione DevOps, la testabilità è un'area di fondamentale importanza. Aiuta ad alleviare i colli di bottiglia nella fase di sviluppo. Tuttavia, è importante gestire le aspettative: i risultati immediati potrebbero non essere possibili, soprattutto quando si ha a che fare con grandi basi di codice legacy. Il refactoring di tale codice senza test di unità e di regressione sufficienti rappresenta una sfida importante.
Lezioni da The Pragmatic Programmer
Per le situazioni in cui non è possibile partire con una nuova base di codice, The Pragmatic Programmer: Your Journey to Mastery, di Andy Hunt e David Thomas offre consigli pratici:
- Puntare a modifiche incrementali: concentrarsi sul refactoring di parti piccole e gestibili del codice.
- Separare i componenti: ridurre le dipendenze per semplificare i test sulle singole unità.
- Suddividere le funzioni monolitiche: dividere le funzioni grandi in unità più piccole e mirate.
- Adottare una progettazione modulare: migliorare la modularità per rendere il codice più facile da testare e manutenere.
Il messaggio è chiaro: una volta risolti i colli di bottiglia iniziali, la testabilità deve diventare un'area chiave su cui concentrarsi. Questo ridurrà i vincoli nella fase di test e consentirà di rilasciare software di alta qualità in tempi più brevi.
Prospettive future
Il nostro percorso per migliorare la testabilità ci ha insegnato lezioni preziose sul ruolo della progettazione del software nel facilitare una transizione fluida verso DevOps. Concentrandoci sulla testabilità, abbiamo risolto i colli di bottiglia, migliorato le competenze degli sviluppatori e gettato le basi per il successo futuro. Sebbene i risultati immediati possano essere lenti, i benefici a lungo termine in termini di qualità, efficienza e crescita del team rendono questo investimento vantaggioso.
Mentre proseguiamo il nostro percorso DevOps, misurare il successo diventa la prossima sfida importante. Nel Capitolo 4 analizzeremo come abbiamo definito KPI e dashboard per monitorare i progressi e individuare ulteriori aree di miglioramento.
Rimanete sintonizzati mentre approfondiamo come il processo decisionale basato sui dati ci abbia aiutato a perfezionare i nostri processi DevOps e a ottimizzare le prestazioni!
