Questa limitazione di Test Driven Development (e Agile in generale) è praticamente pertinente?

26

In Test Driven Development (TDD) si inizia con una soluzione subottimale e quindi si producono iterativamente migliori con l'aggiunta di casi di test e di refactoring. I passi dovrebbero essere piccoli, il che significa che ogni nuova soluzione sarà in qualche modo vicina al precedente.

Questo assomiglia a metodi di ottimizzazione locali matematici come la discesa del gradiente o la ricerca locale. Una ben nota limitazione di tali metodi è che non garantiscono di trovare l'optimum globale, o anche un optimum locale accettabile. Se il punto di partenza è separato da tutte le soluzioni accettabili da una vasta area di soluzioni non valide, è impossibile arrivarci e il metodo fallirà.

Per essere più specifici: sto pensando a uno scenario in cui hai implementato un numero di casi di test e poi scopri che il prossimo caso di test richiederebbe un approccio completamente differente. Dovrai buttare via il tuo lavoro precedente e ricominciare da capo.

Questo pensiero può essere applicato a tutti i metodi agili che procedono a piccoli passi, non solo a TDD. Questa proposta di analogia tra TDD e ottimizzazione locale ha qualche grave difetto?

    
posta Frank Puffer 08.01.2017 - 23:28
fonte

8 risposte

6

A well-known limitation of such methods is that they do not guarantee to find the global optimum, or even an acceptable local optimum.

Per rendere il confronto più adeguato: per alcuni tipi di problemi, gli algoritmi di ottimizzazione iterativa sono molto propensi a produrre buoni optima locali, per alcune altre situazioni, possono fallire.

I am thinking of a scenario where you have implemented a number of test cases and then find that the next test case would require a competely different approach. You will have to throw away your previous work and start over again.

Posso immaginare una situazione in cui ciò può accadere nella realtà: quando scegli l'architettura sbagliata in un modo devi ricreare di nuovo tutti i test esistenti. Diciamo che inizi ad implementare i tuoi primi 20 casi di test nel linguaggio di programmazione X sul sistema operativo A. Sfortunatamente, il requisito 21 include che l'intero programma deve essere eseguito sul sistema operativo B, dove X non è disponibile. Quindi, devi buttare via la maggior parte del tuo lavoro e reimplementare nella lingua Y. (Naturalmente, non buttare via il codice completamente, ma portarlo nella nuova lingua e sistema.)

Questo ci insegna, anche quando si usa TDD, è una buona idea fare qualche analisi generale e amp; design in anticipo. Questo, tuttavia, vale anche per qualsiasi altro tipo di approccio, quindi non lo vedo come un problema di TDD inerente. Inoltre, per la maggior parte delle attività di programmazione del mondo reale è sufficiente scegliere un'architettura standard (come il linguaggio di programmazione X, il sistema operativo Y, il sistema db Z sull'hardware XYZ) e si può essere relativamente sicuri che una metodologia iterativa o agile come TDD non ti porterà in un vicolo cieco.

Citando Robert Harvey: "Non puoi far crescere un'architettura dai test unitari." O pdr: < a href="https://softwareengineering.stackexchange.com/a/102653/9113"> "TDD non mi aiuta solo a raggiungere il miglior design finale, ma mi aiuta a raggiungerlo con un numero inferiore di tentativi."

Quindi in realtà ciò che hai scritto

If your starting point is separated from all acceptable solutions by a large region of bad solutions it is impossible to get there and the method will fail.

potrebbe diventare vero: quando scegli un'architettura sbagliata, probabilmente non raggiungerai la soluzione richiesta da lì.

D'altro canto, quando si pianifica in anticipo la pianificazione generale e si sceglie l'architettura giusta, utilizzare TDD dovrebbe essere come avviare un algoritmo di ricerca iterativo in un'area in cui ci si può aspettare di raggiungere il "massimo globale" (o almeno una buona -assoluto massimo) in alcuni cicli.

    
risposta data 09.01.2017 - 17:18
fonte
19

Non penso che TDD abbia un problema di massimi locali. Il codice che scrivi potrebbe, come hai notato correttamente, ma è per questo che è in atto il refactoring (riscrittura del codice senza modificare la funzionalità). In sostanza, con l'aumentare dei test, è possibile riscrivere parti significative del modello a oggetti se necessario mantenendo il comportamento invariato grazie ai test. Verifica lo stato di verità invarianti sul tuo sistema che, pertanto, deve essere valido sia in locale che in assoluto.

Se sei interessato a problemi relativi al TDD, posso menzionarne tre diversi che penso spesso:

  1. Il problema completezza : quanti test sono necessari per descrivere completamente un sistema? "Codificare per casi di esempio" è un modo completo per descrivere un sistema?

  2. Il problema hardening : qualsiasi interfaccia di test, deve avere un'interfaccia inalterabile. I test rappresentano verità invarianti , ricorda. Sfortunatamente queste verità non sono affatto note per la maggior parte del codice che scriviamo, nella migliore delle ipotesi solo per gli oggetti esterni.

  3. Il problema danno di prova : per rendere testabili le asserzioni, potrebbe essere necessario scrivere un codice subottimale (ad esempio, meno performante). Come scrivere test così il codice è buono come può essere?

Modificato per indirizzare un commento: ecco un esempio di eccitazione di un massimo locale per una funzione "doppia" tramite refactoring

Test 1: quando l'input è 0, restituisce zero

Implementazione:

function double(x) {
  return 0; // simplest possible code that passes tests
}

Refactoring: non necessario

Test 2: quando l'input è 1, restituisce 2

Implementazione:

function double(x) {
  return x==0?0:2; // local maximum
}

Refactoring: non necessario

Test 3: quando l'input è 2, return 4

Implementazione:

function double(x) {
  return x==0?0:x==2?4:2; // needs refactoring
}

Refactoring:

function double(x) {
  return x*2; // new maximum
}
    
risposta data 08.01.2017 - 23:55
fonte
11

Nella sua risposta , @Sklivvz ha argomentato in modo convincente che il problema non esiste.

Voglio sostenere che non ha importanza: la premessa fondamentale (e la ragione d'essere) delle metodologie iterative in generale e Agile e in particolare TDD in particolare, è che non solo l'optimum globale, ma gli ottimums locali come bene non sono noti. Quindi, in altre parole: anche se quello era un problema, non c'è modo di farlo comunque in modo iterativo. Supponendo che ti aspetti la premessa di base.

    
risposta data 09.01.2017 - 01:53
fonte
11

Ciò che stai descrivendo in termini matematici è ciò che chiamiamo dipingere te stesso in un angolo. Questo evento è difficilmente esclusivo per TDD. In cascata puoi raccogliere e superare i requisiti per mesi sperando che tu possa vedere il massimo globale solo per arrivarci e capire che c'è un'idea migliore solo sulla prossima collina.

La differenza è in un ambiente agile che non ti saresti mai aspettato di essere perfetto a questo punto, quindi sei più che pronto a gettare la vecchia idea e passare alla nuova idea.

Più specifico per TDD c'è una tecnica per evitare che ciò accada mentre aggiungi funzionalità sotto TDD. È Locale prioritario della trasformazione . Laddove TDD ha un modo formale di refactoring, questo è un modo formale per aggiungere funzionalità.

    
risposta data 09.01.2017 - 06:04
fonte
7

Le pratiche TDD e Agile possono promettere di produrre una soluzione ottimale? (O anche una "buona" soluzione?)

Non esattamente. Ma questo non è il loro scopo.

Questi metodi forniscono semplicemente un "passaggio sicuro" da uno stato all'altro, riconoscendo che i cambiamenti sono lunghi, difficili e rischiosi. E il punto di entrambe le pratiche è garantire che l'applicazione e il codice siano entrambi fattibili e dimostrati per soddisfare i requisiti più rapidamente e con maggiore regolarità.

... [TDD] is opposed to software development that allows software to be added that is not proven to meet requirements ... Kent Beck, who is credited with having developed or 'rediscovered' the technique, stated in 2003 that TDD encourages simple designs and inspires confidence. (Wikipedia)

TDD si concentra sull'assicurare che ogni "pezzo" di codice soddisfi i requisiti. In particolare, aiuta a garantire che il codice soddisfi i requisiti preesistenti, invece di lasciare che i requisiti siano guidati da una cattiva codifica. Ma non promette che l'implementazione sia "ottimale" in alcun modo.

Come per i processi Agile:

Working software is the primary measure of progress ... At the end of each iteration, stakeholders and the customer representative review progress and re-evaluate priorities with a view to optimizing the return on investment (Wikipedia)

L'agilità non è alla ricerca di una soluzione ottimale; solo una soluzione di lavoro - con l'intento di ottimizzare ROI . Promette una soluzione di lavoro più presto piuttosto che più tardi ; non uno "ottimale".

Ma va bene, perché la domanda è sbagliata.

Gli obiettivi nello sviluppo del software sono confusi, spostando obiettivi. I requisiti sono di solito fluidi e pieni di segreti che emergono solo per il tuo imbarazzo in una sala riunioni piena di boss del tuo capo. E la "intrinseca bontà" dell'architettura e della codifica di una soluzione è calibrata dalle opinioni divise e soggettive dei tuoi pari e quella del tuo padrone manageriale - nessuno dei quali potrebbe effettivamente sapere nulla di un buon software.

Per lo meno, le pratiche TDD e Agile riconoscono le difficoltà e tentano di ottimizzare per due cose che sono oggettive e misurabili: Working v. Not-Working e < em> Sooner v. Later.

E, anche se abbiamo "lavoro" e "prima" come metriche oggettive, la tua capacità di ottimizzare per loro dipende principalmente dall'abilità e dall'esperienza di un team.

Le cose che potresti interpretare come sforzi producono soluzioni ottime includono cose come:

etc ..

Se ciascuna delle quelle cose effettivamente produce soluzioni ottimali sarebbe un'altra grande domanda da porsi!

    
risposta data 09.01.2017 - 15:03
fonte
4

Una cosa che nessuno ha aggiunto finora è che lo "Sviluppo TDD" che stai descrivendo è molto astratto e irrealistico. Potrebbe essere così in un'applicazione matematica in cui si sta ottimizzando un algoritmo, ma ciò non accade molto nelle applicazioni aziendali su cui molti programmatori lavorano.

Nel mondo reale i tuoi test sono fondamentalmente esercizi e convalida delle regole aziendali:

Ad esempio: se un cliente ha 30 anni non fumatore con moglie e due figli, la categoria premium è "x" ecc.

Non cambierai iterativamente il motore di calcolo premium finché non sarà corretto per molto tempo - e quasi sicuramente non mentre l'applicazione è in diretta;).

Ciò che hai effettivamente creato è una rete di sicurezza in modo che quando un nuovo metodo di calcolo viene aggiunto per una particolare categoria di clienti, tutte le vecchie regole non si rompono improvvisamente e danno la risposta sbagliata. La rete di sicurezza è ancora più utile se il primo passo del debug è creare un test (o una serie di test) che riproduca l'errore prima di scrivere il codice per correggere il bug. Poi, un anno lungo la traccia, se qualcuno accidentalmente ricrea il bug originale, il test dell'unità si interrompe prima che il codice sia addirittura archiviato. Sì, una cosa che TDD consente è che ora puoi fare un grande refactoring e riordinare le cose con sicurezza ma non dovrebbe essere una parte importante del tuo lavoro.

    
risposta data 09.01.2017 - 07:57
fonte
3

Non penso che interferisca. La maggior parte dei team non ha nessuno che sia in grado di trovare una soluzione ottimale anche se l'ha scritta sulla sua lavagna. TDD / Agile non si metterà sulla loro strada.

Molti progetti non richiedono soluzioni ottimali e quelli che lo fanno, il tempo, l'energia e l'attenzione necessari saranno fatti in quest'area. Come tutto il resto che tendiamo a costruire, prima, fallo funzionare. Quindi fallo velocemente. Puoi farlo con una specie di prototipo se la performance è così importante e poi ricostruire il tutto con la saggezza acquisita attraverso molte iterazioni.

I am thinking of a scenario where you have implemented a number of test cases and then find that the next test case would require a competely different approach. You will have to throw away your previous work and start over again.

Ciò potrebbe accadere, ma ciò che è più probabile che accada è la paura di cambiare parti complesse dell'applicazione. Non avere alcun test può creare un senso di paura più grande in quest'area. Un vantaggio di TDD e una serie di test è che hai costruito questo sistema con l'idea che dovrà essere modificato. Quando si arriva a questa soluzione monolitica ottimizzata fin dall'inizio, può essere molto difficile cambiare.

Inoltre, inseriscilo nel contesto della tua preoccupazione per l'ottimizzazione non ottimizzata, e non puoi fare a meno di sprecare tempo ad ottimizzare le cose che non dovresti avere e creare soluzioni inflessibili perché eri così concentrato sulle loro prestazioni.

    
risposta data 09.01.2017 - 20:04
fonte
0

Può essere ingannevole applicare un concetto matematico come "ottimo locale" alla progettazione del software. L'uso di tali termini rende lo sviluppo del software un suono molto più quantificabile e scientifico di quanto non sia in realtà. Anche se "ottimale" esistesse per il codice, non abbiamo modo di misurarlo e quindi non c'è modo di sapere se lo abbiamo raggiunto.

Il movimento agile era davvero una reazione contro la convinzione che lo sviluppo del software potesse essere pianificato e previsto con metodi matematici. Nel bene e nel male, lo sviluppo del software è più simile a un mestiere che a una scienza.

    
risposta data 10.01.2017 - 14:14
fonte