Esiste un principio di ingegneria del software che riguarda i costi di riutilizzo e di regressione su un sistema di produzione?

12

Ho lavorato su un grande sistema di transazioni finanziarie per una banca che si occupava di pensioni e investimenti. Dopo 15 anni di modifiche delle funzionalità, il costo del test di regressione manuale era salito a $ 200.000 per versione. (10M LOC, $ 10 milioni di transazioni al giorno). Questo sistema si interfaccia anche con altri 19 sistemi attorno all'azienda che spostano molti dati in giro. Questo sistema è stato implementato in Java.

Ciò che osserviamo tuttavia è che più "riutilizziamo", più aumentano i costi dei test di regressione. (Il motivo è che devi "testare il codice che tocchi" e il codice riutilizzato / condiviso influisce su una molteplicità di punti quando viene toccato, quindi, nonostante "DRY - Non ripeti te stesso" - cioè non copiare e incollare il codice - osserviamo un incentivo finanziario per copiare e incollare il codice, in modo da ridurre i costi del test di regressione, perché non vogliamo modificare il codice che potrebbe essere condiviso, perché ciò causerà un impatto notevole sul test di regressione.)

La mia domanda è c'è un principio di ingegneria del software che descrive la relazione tra i costi di riutilizzo e di regressione?

Il motivo per cui vorrei porre questa domanda è che probabilmente c'è un vantaggio in termini di costi nella scomposizione del sistema in parti più piccole da testare.

Ipotesi:

  1. "Test di regressione" significa "test di accettazione", ovvero un altro gruppo che impiega tempo a scrivere nuovi e riutilizzare i vecchi test sul sistema per conto dell'azienda, incluse le impostazioni di ambiente e dati.

  2. So che la reazione istintiva a un grande costo del test di regressione è "test più automatizzati". Questo è un buon principio. In questo ambiente ci sono un paio di sfide.

    (a) I test automatici sono meno utili oltre i limiti del sistema, a meno che tale sistema non abbia una copertura di test automatizzata elevata. (Sfida sull'influenza).

    (b) È culturalmente difficile dare slancio al tempo del programmatore o all'investimento di capitale su una copertura di test automatizzata elevata quando il sistema è già grande e complesso.

    (c) Il costo per mantenere i test automatici è nascosto in un progetto, e quindi sono facilmente scartati a livello di progetto.

    (d) Questa è solo la realtà culturale di lavorare in una banca.

    (e) Sto lavorando per risolvere questo problema in un modo diverso (decomposizione).

posta hawkeye 29.12.2013 - 22:20
fonte

3 risposte

11

we don't want to modify code that could be shared, because that will cause a big regression test impact

I suoni sopra sono giusti per me. Più il codice è importante, più è condiviso, più elevati sono i requisiti di qualità, più la qualità la garanzia dovrebbe essere coinvolta quando cambia.

Poiché il tuo sistema è implementato in Java, puoi vedere un esempio qui sopra, nelle librerie standard Java (JDK). Le sue versioni principali sono poco frequenti e sono accompagnate da test molto impegnativi. E anche i rilasci minori vengono eseguiti attraverso una JCK suite di test molto completa per verificare l'assenza di regressioni.

Potresti pensare che questo in qualche modo soffochi l'evoluzione del codice condiviso, e ... sì, è giusto. Maggiore è l'impatto e il rischio associati al cambio di codice, maggiore è l'attenzione che si dovrebbe fare nel fare ciò, maggiori sono gli sforzi da compiere per testare le sue versioni.

Idealmente, la qualità delle versioni di un codice ampiamente condiviso dovrebbe essere tale da non aver bisogno di grandi cambiamenti (salvo miglioramenti non frequenti). Questa linea di pensiero si riflette in una citazione famosa di Joshua Bloch :

Public APIs, like diamonds, are forever. You have one chance to get it right so give it your best.

Tuttavia, con quanto sopra detto, alcuni dei problemi che descrivi sono causati da una strategia inefficiente di sviluppo di codice condiviso. In particolare, sembra particolarmente fastidioso che per il codice riutilizzato vengano prese in considerazione solo due opzioni: duplicare questo codice o includerlo immediatamente nelle librerie condivise "core".

Limitare a queste due opzioni non è necessario e, di nuovo, puoi trovare esempi di come può essere fatto meglio nel JDK che usi. Dai un'occhiata ai pacchetti java.util.concurrent ( JSR 166 ) - fino al rilascio di Java 5, questi erano una libreria separata, non una parte delle versioni core di JDK.

Pensaci, questa è una terza opzione che hai trascurato, e abbastanza pragmatica, quella che devi considerare all'inizio del nuovo codice condiviso. Quando si calcola un po 'di codice che può essere condiviso tra 2-3 componenti, nulla ti obbliga a includerlo immediatamente nell'API di base del sistema.

Puoi impacchettare e rilasciare questo codice "immaturo" come libreria separata, proprio come è stato fatto per le utilità concorrenti di Java. In questo modo si evita la necessità di test di regressione completa, poiché è possibile utilizzare solo una quantità relativamente piccola di componenti coinvolti. Di conseguenza, hai più margine di manovra per modificare e migliorare questo codice condiviso e per verificare come funziona in produzione.

Dopo che la libreria è maturata e stabilizzata quanto basta per darti la certezza che ulteriori modifiche in esso siano improbabili, puoi considerarne l'inclusione nelle librerie principali del sistema, proprio come le utilità concorrenti sono state eventualmente incluse in JDK.

Un esempio concreto di quanto impegno (inclusi i test) possa essere coinvolto nel cambio di codice pesantemente riutilizzato può essere trovato, ancora una volta, in JDK. Nella versione 7u6 hanno cambiato la rappresentazione interna di String che comportava una modifica della prestazione substring . I commenti di uno sviluppatore di funzionalità di Reddit descrivono lo sforzo compiuto in questo cambiamento:

The initial analysis came out of the GC group in 2007...

Internally the Oracle performance team maintains a set of representative and important apps and benchmarks which they use to evaluate performance changes. This set of apps was crucial in evaluating the change to substring. We looked closely at both changes in performance and change in footprint. Inevitably, as is the case with any significant change, there were regressions in some apps as well as gains in others. We investigated the regressions to see if performance was still acceptable and correctness was maintained...

My reply is not intended to be exhaustive but is a very brief summary of what was almost six months of dedicated work...

    
risposta data 30.12.2013 - 10:02
fonte
9

Non penso esistano metriche per calcolare "costo per test di regressione / LOC di codice riutilizzato". E non penso che nessuno abbia mai investito così tanto tempo e denaro per costruire due volte lo stesso sistema "grande", una versione con molti componenti reperibili e una senza, per fare una ricerca seria su questo.

Ma ho visto problemi causati dal riutilizzo come il tuo prima, e forse sei interessato a qualche idea su come gestirlo meglio.

Innanzitutto, in realtà non è riutilizzare quale è il tuo problema - è piuttosto il tentativo di costruire i tuoi componenti riutilizzabili e usarli nel tuo sistema. Sono sicuro che stai facendo un sacco di riutilizzo di grandi pacchetti software in cui i tuoi problemi non sorgono: pensa all'intero stack Java che stai usando, o forse a componenti di terze parti (supponendo che tu fossi soddisfatto di quei componenti). Ma cosa c'è di diverso con quel software, ad esempio le librerie Java, mentre i tuoi componenti riutilizzabili ti causano così tanti costi aggiuntivi per i test di regressione? Ecco alcuni punti che credo potrebbero essere diversi:

  • questi componenti sono molto maturi e stabili

  • sono sviluppati e completamente testati separatamente, da un'organizzazione completamente diversa

  • per (ri) usarli, non devi cambiarli (infatti non potresti neanche se ti piacerebbe, dato che non hai il codice sorgente)

  • non ottieni quotidianamente una nuova versione, solo aggiornamenti minori (al massimo al mese) o aggiornamenti importanti in intervalli all'anno

  • la maggior parte degli aggiornamenti è progettata per essere compatibile al 100% al ribasso, in particolare gli aggiornamenti minori

Quindi, per rendere i tuoi componenti riutilizzabili più efficaci, dovresti adattare alcune di queste cose dall'alto per il tuo sviluppo:

  • per qualsiasi componente riutilizzabile, avere una chiara responsabilità chi fa la manutenzione e assicurarsi che tutte le persone che riutilizzano un componente possano essere sicuri di ottenere immediatamente un bugfix in caso di problemi.

  • stabilisce un rigoroso controllo delle versioni e delle politiche di rilascio. Quando si evolve un componente riutilizzabile, non rilascialo "a tutti" tutti i giorni (almeno, non se ciò implicasse l'esecuzione di un test di regressione completo di $ 200K sul sistema). Invece, lascia che le nuove versioni vengano pubblicate di volta in volta e fornisci meccanismi per consentire all'utente di quel componente di rinviare la modifica alla nuova versione.

  • più spesso un componente viene riutilizzato, più è importante che fornisca un'interfaccia stabile e un comportamento compatibile verso il basso.

  • i componenti riutilizzabili richiedono test suite molto completi per testarli separatamente.

Un sacco di queste cose significherà che il costo della costruzione del componente stesso aumenterà, ma diminuirà anche i costi delle modifiche causate da regressioni fallite.

    
risposta data 30.12.2013 - 10:04
fonte
0

Sebbene possa esserci un aumento "osservabile" del costo a causa di ulteriori test necessari, quel tipo di refactoring di solito rende il codice più gestibile in futuro, in quanto si sta riducendo il debito tecnico nel sistema.

Questo dovrebbe sperare di ridurre i futuri bug e rendere più facili da implementare nuove funzionalità o modifiche alle funzionalità esistenti.

Più facile, voglio dire che dovrebbero impiegare meno tempo e quindi costare meno.

Riduci, più facile e meno sono tutti termini piuttosto nebulosi qui e qualsiasi risparmio futuro (o piuttosto sperato per il salvataggio) è impossibile da calcolare in quanto non è ancora successo.

Una base di codice più semplice dovrebbe consentire ai nuovi dipendenti o ai dipendenti esistenti di passare al progetto di accelerare più rapidamente, soprattutto per i sistemi di grandi dimensioni.

Può anche ridurre il turnover del personale in quanto i membri del progetto esistenti possono migliorare il morale.

Ovviamente non è garantito che otterrai questi benefici, ma queste sono cose che dovrebbero essere considerate a fianco dei costi (come un aumento dei test) che possono essere misurati.

In effetti, un codice migliore dovrebbe alla fine ridurre i costi dei test nel tempo, anche se c'è un aumento iniziale dovuto a ciò che descrivi.

    
risposta data 10.01.2014 - 10:41
fonte

Leggi altre domande sui tag