Sviluppo “Inspirato” AGILE… e così: SIA

Una personale visione per migliorare la produttività e la collaborazione di noi “artigiani del codice”, e (spoiler) motivazioni non ovvie della vera importanza dei test!

AGILE disclaimer :p

compcat

Quando nel titolo di un articolo compare la parola “agile” (cercando per di più di fare un acronimo) si tocca un tema che ha al suo seguito millemila idee. Una buonissima cosa: stimola la discussione e ci porta a riflettere su come lavoriamo e come migliorarci.

Troppo spesso però ho riscontrato un certo fanatismo nell’affrontare questo argomento, trovando interlocutori con atteggiamenti evangelici di chi vuole diffondere il Verbo e liberare il programmatore dal vecchio, obsoleto, inefficace ed inefficiente metodo di lavorare, ammonendo ogni altro sviluppatore che non segue la Via, ostentando in più orgogliosa illuminazione.

Quando qualcuno mi propina la “metodologia perfetta” per la soluzione ad ogni problema, senza avere neanche rielaborato personalmente, ne tantomeno sperimentato motivazioni, benefici, scenari, divento per natura piuttosto scettico.

Per questo motivo quando leggo “AGILE” ho le antenne sempre piuttosto in allerta, e per questo motivo il mio contributo sull’argomento vuole solo condividere una personale valutazione ed esperienza che credo possa aiutare noi “artigiani del codice”. In linea con questa accezione, che sottolinea l’importanza della soggettività del processo creativo di uno sviluppatore, è proprio l’idea di uno degli autori dell’originale manifesto: David Thomas – Agile Is Dead (Long Live Agility) assolutamente da leggere!

Ispirazione

Veniamo a noi: più di un anno fa mi è capitato sotto mano un libro: Clean Code – A handbook of agile software craftmanship. Non ho fatto molto caso al sottotitolo agilistico, quello che mi interessava era appunto migliorarmi e carpire qualche trucco per rendere il mio codice sempre più pulito.

L’autore è partito da osservazioni che condivido e che ho riscontrato nella mia esperienza; parafrasando:

  1. quando lavoriamo passiamo mooooolto tempo a leggere codice, piuttosto che a scriverlo (fatevi uno screencast!)
  2. per fare in modo di lavorare più velocemente è necessario essere più veloci a leggere il codice, ergo: il codice deve essere il più leggibile possibile!

Nella prima osservazione, mi ci ritrovo pienamente: leggo ovviamente tantissimo per vedere dove devo toccare, quale design implementare/modificare, quali nomi scegliere etc.

Anche la seconda osservazione è assolutamente lineare, perché se si vuole che un progetto abbia lunga vita (escludendo per ora questioni architetturali) deve essere manutenibile nella sua essenza, equivalente a: deve essere scritto bene!

La domanda a questo punto, nasce spontanea: come faccio a rendere il codice più leggibile?
La risposta: un discreto numero di accorgimenti, ma soprattutto… REFACTORING!

REFACTORING

E’ abbastanza improbabile che alla prima stesura si scriva il codice più bello, pulito e parlante che si possa ottenere. Quello che si può fare sono una serie di refactoring progressivi: rinominare variabili/metodi, fattorizzare classi, estrarre funzioni, etc, così che il codice, ad ogni revisione aderisca sempre più a quegli accorgimenti volti a renderlo più leggibile.

In effetti mi sono sempre chiesto: “perchè se su un software ci mettono mano X persone in Y anni il risultato tende spesso a un guazzabuglio ingestibile?”, non dovrebbe essere così!

Il libro accenna al principio: lascia le cose meglio di come le hai trovate. Questi continui refactoring devono essere quindi fatti da ogni sviluppatore in modo che il codice MIGLIORI ogni volta che ci si mette mano, e non il contrario.

C’è un problema: conosciamo tutti il detto “se funziona quanto basta, non toccar sennò si guasta” (corollario: “soprattutto il venerdì pomeriggio” — Alessandro Tiso). Per poter procedere a tutti questi refactoring progressivi, in piena libertà e sicuri di non rompere niente, esiste un solo modo: il testing!

TESTING

E inutile che ce la raccontiamo, senza dei test completi, i casi sono due:

  • limitiamo/minimizziamo il lavoro di refactoring (che possiamo ormai chiamare pulizia, riportandoci sulla via del degrado del codice)
  • oppure rompiamo qualcosa… senza neanche accorgercene subito!!!

L’obiezione, più che ragionevole, che spesso si fa riguardo ai test : ma serve un sacco di tempo ed energie per scrivere e mantenere i test!.

Queso è vero se: “you’re doing wrong!”. Se per scrivere un test ci metto un sacco di tempo: lo sto facendo nel modo sbagliato! Cosa significa questo? Significa che anche, e soprattutto, nei test devo essere in grado di scrivere codice leggibile, devo poter esprimere un singolo caso da testare in 3-5 (dico sul serio!) righe di codice:

  1. preparazione
  2. esecuzione metodo da testare
  3. verifica dopo il test

Per esprimere tutto in 3 righe di codice è chiaro che dovrò fattorizzare/strutturare i test in modo che siano altamente leggibili. Fatto un po’ di sforzo iniziale sul design dei test (anche questi con l’aiuto di refactoring progressivi), l’aggiunta di nuovi casi di test può diventare un lavoro davvero minimo.

I test dovranno insomma essere “primi cittadini” al pari del sistema sviluppato, e tutto ciò si ripercuoterà in un generale miglioramento di tutto il codice!

La novità (almeno per me) di questa interpretazione sta proprio in questo: i test sono importanti quanto l’applicazione stessa per poter garantire codice pulito su cui si potrà continuare a lavorare agevolmente e velocemente.

Un altro contributo importante che danno i test è quello di formare delle specifiche chiare, anzi, che dico: compilabili! Se ho bisogno di richiedere a uno sviluppatore di implementare una nuova funzionalità, non c’è cosa migliore di scrivere dei test che definiscano le interfacce e comportamenti desiderati. Non c’è ambiguità in un test: o passa o fallisce (ahhh il tanto beneamato bit!)

INspirazione

Digerito tutto quanto scritto qui sopra, la cosa da fare è inspirare e tuffarsi IN questa modalità TDD (Test Driven Development) e partire proprio dai test. È un po’ dura lo so, ma ne vale la pena secondo me. Alla prima stesura ci si trova di solitamente di fronte a un bel “metodone” di test che inizia con “test” che a guardarlo sappiamo già che faremo fatica a metterci mano il giorno che modificheremo l’applicazione .

Ecco che si inizia quindi a pulire il codice dei test, e che tornano utili tutti quegli accorgimenti che si imparano con l’esperienza e che il signor Robert C. Martin ha descritto nel libro. Ne accenno qui solo alcuni che per me sono i più importanti:

NIENTE METODI PIÙ LUNGHI DI 15 RIGHE!!!

E mi sono tenuto largo. Prima che leggessi il libro anche Mario Fusco a un incontro del JUG di parecchi anni fa promuoveva questa buona buona abitudine. Sembra perentorio lo so… ma solo 5 righe di codice messe insieme fanno già sicuramente un mestiere, quel mestiere merita un nome! (e non un commento sopra).

Con IntelliJ (ma sicuramente anche altri IDE) basta selezionare le righe e con CTRL+ALT+M si estrae il metodo.

1 METODO => 1 COSA

L’idea di fondo, legata all’accorgimento precedente che ho poi ritrovato nel libro, è quella che un metodo deve fare una, e una sola cosa. E’ un’astrazione/semplificazione invocata da un livello di astrazione più alto ed è importante che faccia quello che il suo nome descrive, nulla di più e nulla di meno.

Se un metodo fa più cose, è probabile che ci stiamo perdendo un livello di astrazione che può semplificare notevolmente il riutilizzo, lettura e manutenzione del codice!

NOMI (E NON DIRE BUGIE)

There are only two hard things in Computer Science: cache invalidation and naming things.
— Phil Karlton

I nomi sono forse la cosa più importante per quanto riguarda la leggibilità, un esempio preso dal libro lo dimostra in maniera efficace:

public int x() {
	int q = 0;
	int z = 0;
	for (int kk = 0; kk < 10; kk++) {
		if (l[z] == 10)
		{
			q += 10 + (l[z + 1] + l[z + 2]);
			z += 1;
		}
		else if (l[z] + l[z + 1] == 10)
		{
			q += 10 + l[z + 2];
			z += 2;
		} else {
			q += l[z] + l[z + 1];
			z += 2;
		}
	}
	return q;
}

Rinominando le variabili

public int score() {
	int score = 0;
	int frame = 0;
	for (int frameNumber = 0; frameNumber < 10; frameNumber++) {
		if (isStrike(frame)) {
			score += 10 + nextTwoBallsForStrike(frame);
			frame += 1;
		} else if (isSpare(frame)) {
			score += 10 + nextBallForSpare(frame);
			frame += 2;
		} else {
			score += twoBallsInFrame(frame);
			frame += 2;
		}
	}
	return score;
}

e  metodi “parlanti”

private boolean isStrike(int frame) {
	return rolls[frame] == 10;
}

I nomi per i metodi sono ancora più importanti, e devono avere una caratteristica fondamentale: “non dire bugie”. Se un metodo oltre a fare una cosa fa anche altro, sta mentendo. Immaginiamo ad esempio che il codice nell’esempio sia utilizzato nel tabellone di uno stadio, e che ad ogni strike si voglia far sentire un motivetto per la gioia dei tifosi.

private boolean isStrike(int frame) {
	boolean isStrike = rolls[frame] == 10;
	if (isStrike) playStrikeMusic();
	return isStrike;
}

Ecco che ad un ricalcolo completo del punteggio forse si sentirebbe qualche motivetto di troppo. Rinominando il metodo in “isStrikeAndPlayMusic()” ecco che in “score()” sarebbe subito evidente che qualcosa non va, e indicando che forse quello non è proprio il posto giusto per la modifica.

ALTRI ACCORGIMENTI…

Li trovate in questo libro fonte davvero di ispirazione. Nei prossimi post? Forse qualche esempio di software reale potrebbe anche scapparmi!

Leave a Reply

Your email address will not be published. Required fields are marked *