When was the last time you did something for the first time?
Other Languages:
L’input che ha dato il via all’intero ragionamento, parte da un’osservazione che chiunque abbia sviluppato secondo la metodologia Test Driven Development (TDD) ha incontrato, prima o poi.
Consideriamo per un attimo di non impiegare gli opportuni pattern di refactoring altrimenti necessari.
Giorno 1
Definiamo il test per una funzione A;
Il test passa (green bar, secondo la terminologia di Beck).
Giorno 10
Dopo aver usato la funzione A in diversi punti del codice, ci accorgiamo che A ha bisogno di una leggera variazione, ad esempio un nuovo parametro in ingresso, impiegato opportunamente nel codice.
Modifichiamo di conseguenza la funzione e tutte quelle che la invocano.
Questa modifica al resto del programma la eseguiamo senza seguire un particolare pattern di refactoring, come premesso. Questo perchè gli errori di codifica possono sfuggire a tutti, ed in questa sede c’interessa valutare proprio quegli errori indotti dalla distrazione, o dalla naturale imprecisione dell’uomo.
Un Refactoring applicato male è peggio di un refactoring non applicato per niente.
Il test di A passa anche stavolta, altra green bar (GB).
Giorno 20
Ci serve un’altra funzione, chiamata B, che usa A al suo interno.
Testiamo B ed anche A, con esito positivo (GB).
Giorno 180
Dopo qualche mese passato a costruire la nostra applicazione, in cui abbiamo realizzato centinaia d’altre funzioni che usano A e B, ci accorgiamo che ci serviva un altro parametro in B.
Modifichiamo B e (ovviamente) tutto il codice che la invoca, altrimenti non compiliamo.
A questo punto, il test di A non passa più: red bar.
Il Punto
Strano? In realtà questo comportamento è possibile ed è decisamente ricorrente, in quanto anche se A non invoca direttamente B, essa può dipendere indirettamente tramite tecnologie correlate pertinenti ad altri domini architetturali, come un database in comune.
Tralasciando la complessità esigua dell’esempio, personalmente comportamenti di questo tipo mi hanno indotto ad alcune domande:
1. Cosa ha fatto variare il comportamento del test di A?
2. Se non avessi usato un test, avrei potuto comprendere in tempo le ripercussioni che B ha avuto su A e su tutte le funzioni che usano A, nonostante le relazioni di chiamata diretta non avrebbero evidenziato nulla?
3. Se avessi applicato correttamente il refactoring, avrei dovuto ragionevolmente attendermi un’altra green bar anche al giorno 180?
4. Senza test, mi sarei accorto in tempo o “troppo tardi” del “caos” introdotto nel codice da una mia maledettissima svista?
5. Se la modifica “caotica” fosse stata introdotta da un mio collega disattento (o ignaro del contesto perchè mi ero dimenticato di documentare del codice), avrei avuto la stessa “fortuna” nell’accorgermi dell’errore anche senza test?
Fortuna, sviste (maledette), colleghi maldestri, stanchezza, refactoring applicati male…sono davvero cose da “lamer“, oppure sono elementi che ricorrono anche tra i cutting-edge senior developer?
by Paolo Rainone
5 People had this to say...
Non so se accade ai cutting-edge senior developers, però a volte capita ai Cowboy Coders ;*]
…in base a quello che hai detto, penso:
# Considerazione n°1 - errore nel refactoring -> da qui inizio del “casino”
# Considerazione n°2 - red bar al 180° giorno -> i test ti salvano e ti indicano la “retta via”
Come evitare la considerazione n° 1, beh, finchè saranno gli uomini a scrivere il codice (e non un macchina) forse ci saranno tanti “punti uno” simili e affini…
La considerazione n°2 ti dice che, alla fine, il TDD è una bella cosa… se non altro ti ha segnalato il “caos” che stavi (spero non stai =P) introducendo nel tuo progetto.
Rispondo, quindi, (secondo la mia (poca) esperienza) alle tue giuste domande:
1. il fatto che A non era poi così “figa” e non ha retto alle operazioni di B (come appunto, nell’esempio che hai citato tu, nell’interagire sul DB)
2. dipende dai contesti… penso che nella maggior parte dei casi non te ne saresti accorto… ma chi può dirlo in un contesto così generico? Comunque un Test (anche fatto male, per sviste o incopetenza) dà sempre quel pochetto un più…
Senza nessun test automatico o ti provi (da interfaccia?!?) quello che fà la tua funzione e poi nascondi (o addirittura)togli proprio questa prova (bleah!) oppure resta solo l’ipotesi che non provi proprio niente su come vanno le cose. Il risultato? …ai posteri clienti del tuo progetto (porelli) l’ardua sentenza!
3. come per la 2, dipende dal contesto, ma anche qui (come nel caso precedente) la statistica ti viene incontro e ti dice “si”.
4. senza test te ne saresti accorto, magari, solo un paio d’anni dopo il rilascio del progetto… nel senso che potevi accorgertene subito come anche (più probabilmente) no… Ma le leggi di Murphy ti vengono incontro e… sempre per come la vedo io… e sempre in un contesto così generico… la risposta è “troppo tardi”.
5. scrivi codice “autoesplicativo” (e meno commenti che poi magari non vengono letti) -questo è il mio consiglio “generico”- e… chi sa… chi può dirlo… mi rifaccio alle mie risposte precedenti per risponderti anche al punto 5.
Per concludere, secondo me, fortuna e sfortuna, sviste (maledette e stramaledette), colleghi maldestri (in particolar modo), stanchezza, e chi più ne ha, più ne metta, sono cose a cui è immune solo Dio (o Maometto o Zeus o…), ammesso che esista…
Dovremmo imparare dai nostri errori (ci saranno sempre!) e/o dalle esperienze di altri (infatti adoro questo blog… e anche queste, per fortuna, ci saranno sempre…); solo la conoscenza e l’esperienza possono darti quel pò in più…
…inoltre confido molto nell’uso di approcci agili per sviluppare l’intero progetto e refactoring unito a test automatici su tutto per lo sviluppo…
Ma non illudiamoci: per avere buoni risultati servono sempre e comunque le palle, di base…
L’ulteriore soluzione ai nostri problemi è ancora da scrivere… (forza Paolo!)
I cowboy coders sono i miei preferiti! Arrivano sempre nel momento del bisogno…
“A non era poi così figa”
ma nel contesto specifico, quando A è stata scritta non esisteva il codice di nessun’altra funzione…
Anche a patto di avere a priori un design dettagliato tanto da definire ogni parametro di ogni metodo di ogni classe, con la documentazione a priori di ogni metodo (oh mio dio!), cmq il codice effettivo (quello che il compilatore compila…) di tutto il resto non c’è ancora.
Allora, mi chiedo: come fai a prevedere che quella somma (if, switch, eccezione, scelta di tipo, impiego di quel paricolare indice in una query) si riveli una scelta sbagliata tra 6 mesi?
Ah si…ora che ci penso…in effetti esistono le sfere di cristallo…poi c’è UML…insomma, è facile!
“sviluppare l’intero progetto e refactoring unito a test automatici”
test automatici? e dove stanno? I Wizard di VS2005 e Delphi2006? Belli quelli…
forse zentest è un tool interessante, se sviluppi in Ruby.
E cmq, per automatizzare i test devi avere il codice scritto…quindi al massimo fai test di copertura (la cui scrittura è cmq assistita da diversi tool, NCover, Clover ed ora il nuovo VS2005 for Testers per fare qualche esempio).
Ma la copertura non basta (come cercherò di spiegare in un prossimo post), perchè non ti salva da codice che si è “dimenticato” di implementare business rule.
Puoi testare automaticamente solo ciò che scrivi, ma nulla si può fare su ciò che non scrivi…o ti dimentichi di scrivere.
Per la correttezza tra codice e business rules generalmente sono ancora insostituibili le sessioni di code review, magari estremizzate dal pair programming, ma nulla di automatico…
Want your say?
You must be logged in to post a comment.