saverioriotto.it

Codice Legacy: sicurezza in ambienti Agile

All'inizio dei progetti le nostre applicazioni non sono adeguatamente coperte da alcun tipo di test automatizzato. Nella maggior parte dei casi, le organizzazioni non hanno in programma di buttare via quelle basi di codice e ricominciare da zero, il che significa che bisogna progettare un piano per affrontare il problema della qualità del codice legacy.

Codice Legacy: sicurezza in ambienti Agile

Se lavori nell'IT, "codice legacy", è un termine che sentirai spesso nella tua carriera. Gli sviluppatori ne parlano molto, di solito, con molte connotazioni negative. Molte aziende applicano metodologie agili nei loro progetti, ma all'inizio dei progetti di coaching più agili, scopriamo che le applicazioni dei nostri clienti non sono adeguatamente coperte da alcun tipo di test automatizzato.

Nella maggior parte dei casi, le organizzazioni non hanno in programma di buttare via quelle basi di codice e ricominciare da zero, il che significa che dobbiamo progettare un piano per affrontare il problema della qualità nel codice legacy.

In che modo i team gestiscono il codice legacy per stabilizzare la qualità e migliorare la consegna complessiva del prodotto? Questo sarà l'argomento principale di questo articolo. 

Quando lavoriamo con codice legacy

Il termine "codice legacy" è utilizzato nel campo della tecnologia software. Si riferisce al vecchio codice che di solito non è più in fase di sviluppo attivo. In un certo senso, il codice legacy è l'antitesi del codice pulito, facile da comprendere, mantenere e adattare. Il codice legacy è ingombrante, obsoleto e disordinato, il che può causare numerosi problemi. Il significato esatto del termine dipende molto dalla prospettiva dello sviluppatore e dalla situazione attuale. Tuttavia, in generale, il codice legacy è caratterizzato dai seguenti aspetti:

 - Il codice non è più gestito dallo sviluppatore originale
 - Il codice è stato originariamente scritto per i sistemi operativi che non sono più supportati
 - Non è possibile utilizzare test automatici per trovare bug

In un ambiente agile, i test sono la documentazione più importante. Ci sono molte organizzazioni in cui vengono segnalati più di tre bug critici a settimana per team di sviluppatori, il che distrugge ogni possibilità di seguire un piano specifico.

Gli sviluppatori hanno l'idea che la correzione dei bug sia noiosa, estenuante e deprimente. Descrivere scenari e scrivere test automatizzati prima di sviluppare una user story garantisce che tutti abbiano la stessa visione di ciò che deve essere sviluppato. Queste attività non solo riducono al minimo la necessità di scrivere nuovi bug a causa di rielaborazioni o scenari non presi in considerazione, ma anche, ed è l'obiettivo più importante, sono viste come una parte necessaria dello sviluppo di applicazioni robuste.

Quando lavoriamo con codice legacy, i tipi di test più preziosi sono i test di accettazione (test black-box). Non abbiamo bisogno di un codice ben scritto; non c'è alcuna dipendenza per iniziare a scrivere test automatici sulla nostra interfaccia applicativa. 

Non solo scriviamo test di accettazione, ma sfruttiamo anche questa rete di sicurezza creata con i test di accettazione e iniziamo il refactoring della nostro codice.

Questo refactoring dovrebbe essere incentrato sull'abilitazione della scrittura di unit test e di integrazione o sulla riscrittura di componenti altamente buggy.

L'obiettivo non è solo abbellire o semplificare la lettura del codice, ma anche ottenere risultati positivi! Abbiamo deciso di scrivere test automatizzati perché crediamo che porterà un vantaggio: meno bug segnalati, quindi meno perdita di denaro per la nostra organizzazione a causa di quei bug.

Passaggi strategici che dovremmo adottare per selezionare una parte di codice per i test?

Passaggio 1: scegli il tuo stack tecnologico

Quali strumenti, linguaggio di programmazione o framework utilizzerai per scrivere i tuoi test? Questa è l'architettura di base dei tuoi test e, come tale, sarà davvero difficile cambiarla se fai la scelta sbagliata. A questo punto, ti suggerisco di coinvolgere il tuo Chief Technology Officer (CTO) o i leader tecnici per prendere questa decisione. È importante avere a bordo le persone giuste.

Passaggio 2: preparare una suite di integrazione continua

I nostri test dovrebbero essere eseguiti dopo aver preparato il tuo strumento di integrazione continua (CI). L'esecuzione di tutti i nostri test non dovrebbe richiedere più di 10-20 minuti o un tempo minimo affinché gli sviluppatori eseguano una revisione del codice in un commit relativo a una piccola user story. Questi test dovrebbero essere scritti nel modo più indipendente possibile. Non dovrebbero essere fragili; l'installazione, lo smontaggio e la logica di test stessa dovrebbero essere progettati correttamente per garantire che diversi test eseguiti contemporaneamente non diano falsi allarmi. Dovremmo essere pronti per parallelizzare l'esecuzione.

Passaggio 3: definisci i livelli di priorità per le tue applicazioni

La priorità deve essere concordata in tutta l'organizzazione (questo punto è molto importante). Cerca di mantenerlo il più semplice possibile. Idealmente, avrai solo tre livelli di priorità (Alta (P1), Media (P2), Bassa (P3)).

Per esempio:

Gli incidenti ad alta priorità devono soddisfare almeno una di queste condizioni:

 – Il sistema è inattivo

 – Sistema gravemente danneggiato

In questo caso, metteremo tutta la nostra energia in qualsiasi momento e non ci fermeremo finché non avremo risolto il problema e alla fine risolveremo il bug del codice che lo ha causato.

Impatto medio. L'applicazione conintua a funzionare con funzionalità limitate:

 - Sistema danneggiato. In questo caso, pianificheremo che questo bug venga corretto durante il prossimo sprint. Verrà data la priorità al resto delle segnalazioni.

 - Basso impatto. L'applicazione funziona correttamente; tuttavia non vi è alcun difetto estetico o non critico:

 - Sistema leggermente danneggiato. Questo caso verrà risolto alla fine, a un certo punto, in base alla nostra politica sui bug.

E' molto importate capire che ogni parte coinvolta deve essere d'accordo e puntuale nel rispettare questi punti.

Passaggio 4: elenca le caratteristiche del tuo prodotto

Definisci funzionalità di alto livello e assegna loro priorità. Se ad una funzione è assegnata P1, approfondiscila. Inizia a dividere le funzionalità oppure crea nuove funzionalità all'interno della funzionalità P1 come user story assegnando una priorità a ciascuna di essa.

Se una funzione è P2, ma è una parte dell'applicazione modificata di frequente, denominare la funzione P2UP. Per quei P2UP, scrivi le user story contenute in quella funzione. Ancora una volta, assegna le priorità a ciascuna user story.

Qualsiasi funzione con priorità P1 o P2UP è quella per la quale scriverai test automatici. A questo punto, se prevedi di eseguire i test di regressione (manualmente) prima del rilascio, dovresti descrivere tutti gli scenari. Se non hai intenzione di farlo, puoi descrivere gli scenari come parte dell'automazione di ogni user story.

Passaggio 5: sviluppare test di accettazione

Inizialmente non punteremo a una percentuale specifica di copertura del codice in termini di test di unità o integrazione. Il nostro obiettivo iniziale è quello di coprire il 100% di P1 automatizzato e il 100% di P2UP. Il modo migliore per scrivere i test di accettazione automatizzati è utilizzare qualsiasi implementazione di Gherkin, sebbene esistano implementazioni in molti linguaggi di programmazione.

Gestione dei test

In questa fase, dovresti avere un backlog di automazione dei test già prioritario. Ricorda che si tratta di un live backlog, perché a ogni nuova funzionalità che scrivi deve essere assegnata una priorità.

Se una nuova user story è P1 o P2UP, dovrebbe anche essere automatizzata e, se il tempo lo consente, dovrebbe automatizzare il resto degli scenari di test.

Ogni team definisce come realizzare il lavoro per ogni implementazione, idealmente, tutti i membri del team dovrebbero dedicare tempo all'automazione di alcuni test. Tuttavia, l'unica persona che lavora sui test automatizzati è gravata dalla responsabilità di garantire la qualità del prodotto. Questo di solito porta a risultati peggiori.

Non appena decidi di scrivere test automatizzati, inizia a monitorare l'impatto che ha con il tuo lavoro.

Di seguito sono riportate alcune correlazioni particolarmente interessanti per verificarne l'impatto:

 - Tra test eseguiti prima di ogni release e nuovi bug P2 segnalati
 - Tra test eseguiti prima di ogni release e percentuale di tempo dedicato allo sviluppo di nuove funzionalità
 - Tra unit test/integration test e bug rilevati nella tua suite CI

Scrivere test di unità o di integrazione

Dopo aver creato una rete di sicurezza con i test di accettazione automatizzati, è possibile eseguire il refactoring delle parti più difettose dell'applicazione. A questo punto puoi iniziare ad espandere il tuo codice di automazione.

Raggiungere una copertura del codice del 100% con l'integrazione/unit test è estremamente complesso nelle applicazioni legacy e non sempre ne vale la pena.

L'obiettivo principale è concentrarsi sulla copertura delle funzionalità con test di accettazione automatizzati e scrivere solo test unitari/di integrazione per quelle parti dell'applicazione che vengono modificate frequentemente.

In generale, gli sviluppatori mirano a ottenere una copertura del 100% in unità/integrazione. Tuttavia, a seconda delle dimensioni dell'applicazione e delle circostanze attuali, non vale la pena dedicare tempo a scrivere quei test per parti non critiche o parti che non vengono mai modificate. È più intelligente scrivere test di integrazione/unità per quelle parti dell'applicazione che sono critiche e cambiano continuamente, il che ci porta alle user story definite come P1 o P2UP.

Conclusione

Quando ci imbattiamo in Legacy Code, dobbiamo progettare un piano che garantisca la qualità di quel codice e possiamo garantirlo solo se lo copriamo con TEST automatizzati. Per definire le parti del codice che andremo ad automatizzare, dobbiamo prima dividere la nostra applicazione nelle sue funzionalità e stabilire una priorità per esse. Per il momento sarebbe sufficiente garantire la copertura del codice delle funzionalità classificate come la priorità più alta o le funzionalità che includono componenti che cambiano frequentemente, poiché nella maggior parte dei casi non avremo il tempo di ottenere una copertura del 100% di tutto il nostro codice.




Commenti
* Obbligatorio