GUIDE

Debugging: la maledizione del programmatore

LEGANERD 040578

LEGANERD 041520

Non appena iniziammo a programmare, scoprimmo con nostra sorpresa che ottenere programmi corretti non era cosi’ facile come avevamo pensato. Si dovette scoprire il debugging. Ricordo l’istante esatto in cui realizzai che gran parte della mia vita da allora in poi avrebbe dovuto essere spesa nella ricerca degli errori nei miei propri programmi. [Maurice Wilkes scopre il debugging, 1949]

L’avevo promesso a Zed ed io mantengo sempre le mie promesse [cit.]
Eccoci arrivati al settantatreesimo quinto appuntamento con la rubrica ProPa, Programmazione: Parliamone. Abbiamo finora sparlato e chiacchierato di cosa siano i linguaggi di programmazione, cosa li forma e come vengono trasformati perché la macchina li possa effettivamente leggere. No, non ti sto dicendo che devi andare a leggere i precedenti episodi. Non lo sto dicendo. No! Ma te lo ripeto! No! NO! Oh, va bene, cosa ti devo dire? Fai quello che ti pare: leggiteli, analizzateli, studiateli, dormici insieme. E No, io non sono un maestro dei messaggi subliminali tu non hai visto niente.
Ma partiamo dal principio.

I bug: questi sconosciuti

“Come questi sconosciuti!?” vi starete chiedendo. Ebbene sì, scordatevi tutto quello che avete sentito fino ad oggi, poiché solamente i veri programmatori hanno visto in faccia la morte per ben più di una volta e le leggende metropolitane, almeno per una volta, non sono neanche paragonabili alla verità. Come citato all’inizio dell’articolo, il tempo speso per eliminare questi “bachi” è per la maggior parte delle volte decisamente superiore a quello utilizzato per creare il vero e proprio programma. Inoltre, il numero di errori presenti all’interno di un più o meno complesso software aumenta esponenzialmente all’aumentare delle righe di codice; e sono assurdamente riduttivo.
Essi possono essere composti da errori matematici (divisioni per 0, overflow e underflow aritmetici), errori logici (cicli e ricorsività non voluti, valori booleani errati), errori di sintassi (operatori sbagliati) e molto altro ancora. Indipendentemente dal tipo di progetto, sono solitamente più o meno risolvibili ma, quando il codice inizia a diventare davvero massiccio, milioni di righe, neanche un dragone prismatico riesce più ad eliminare un bug senza crearne altri dieci. Infatti, nella quasi totalità dei casi, i programmi vengono rilasciati con alcuni bug già conosciuti ma irrisolti che però, teoricamente, non hanno lo sgradevole effetto di far implodere la macchina e che quindi possono convivere temporaneamente con l’ecosistema software e hardware.

Lo sfigato che tenta di risolvere i bug

Questi errori devono comunque essere risolti, poiché un buon programma deve funzionare appieno ed in assenza di limitazioni. Arriviamo quindi alla tanto odiata fase di debugging. E’ difficile spiegare come si possa arrivare a questa fase senza alcune basi di metodologie e sviluppo di software, però per il momento potete immaginare la fase di debugging in tre differenti fasi: la prima fase, può sembrare scontato ma non lo è affatto, avviene direttamente durante la scrittura del codice iniziale, dove vengono risolti i primi errori mentali che il programmatore generalmente, in assenza di una pianificazione veramente completa ed articolata, trascrive e riconosce senza fatica (inoltre, gli errori grammaticali vengono già evitati grazie al’azione del compilatore); la seconda fase è quella che porta i coder a proporre i software ai tester già direttamente dalle versioni instabili (alpha, beta, etc…) che aiutano a svelare bug critici che non permettono un adeguato funzionamento, per ottenere una release che raggiunga l’obiettivo stabilito in partenza; la terza ed ultima fase è quella che, insieme alla manteinance, contribuisce a chiudere i bug più importanti e ad aprirne quasi sicuramente di nuovi a causa dell’azione di aggiornamento che i developer devono compiere se vogliono mantenere il programma up-to-date [Poi se il software è semplice, compie una sola operazione ed usa 1MB di RAM, lo si lascia così com’è e si passa ad altro!].
In tutte queste differenti fasi, la figura dello sfigato debugger sale in superficie e diventa estremamente importante, se non determinante, per la buona riuscita del progetto. Il problema è che, essendo sfigato, il suo salario sarà sempre proporzionalmente inverso al numero di bug risolti.

Quando il gioco si fa duro, tutti iniziano a scappare

#define TRUE FALSE //Happy debugging suckers

Se il lavoro di debugging fosse così estremamente semplice, quasi nessuno si sognerebbe di lamentarsi. Il fatto è che non è così dannatamente semplice poiché, si sa, noi nerd siamo anche un po’ bastard-inside e facciamo anche a gara per chi riesce a dimostrarsi tale in modo più spettacolare. I debugger sono ostacolati, nella maggior parte dei casi, dalla più o meno volontaria illeggibilità del codice che un programmatore produce. Esistono, infatti, programmatori cresciuti a pane e Orwell che decidono di rendere il proprio codice sporco e confusionario per combattere un nemico che non esiste o a causa di quella vena di sadismo che dopo ore di lavoro esce sempre fuori. Si narra addirittura, nelle più antiche leggende, che il predatore naturale di queste stralunate bestie sia proprio il solitario, caffeinomane, notturno, programmatore addetto al mantenimento del codice che, utilizzando una serie di tecniche zen (ed un po’ di magia), riesce quasi sempre a trovare quello che sta cercando.
La maggior parte della magia è fatta dai debugging tools, programmi adibiti ad aiutare il programmatore a gestire gli errori, attraverso tecniche di monitoraggio dell’esecuzione, della memoria (utlie soprattutto nei linguaggi di basso livello e nel C) e dei cambiamenti di valore. Molto spesso, soprattutto per chi lavora anche in ambito hardware, i migliori amici diventano l’oscilloscopio, un macchinario per rilevare le onde ed i segnali, l'analizzatore di stato logico ed il simulatore di circuiti.
Ovviamente, anche questi tool si stanno evolvendo e piano piano, grazie alla ricerca sulle AI, riusciranno sicuramente a migliorare drasticamente la vita di questi poveri lavoratori o a distruggerli insieme a tutto il resto dell’umanità.

Quando il programmatore è simpatico e coccoloso

Starete dunque immaginando ma sono sicuro di avervi sopravvalutato come un coder possa effettivamente aiutare a rendere più semplice e veloce questo importantissimo processo.
Solitamente, si fa riferimento a delle tecniche che nel corso della storia sono diventate degli standard entrando a far parte delle conoscenze richieste. Il programmatore deve sempre:

Utilizzare un sistema di indentazione che permetta di scorgere facilmente sistemi ad albero, funzioni e cicili. All’inizio del progetto, inoltre, se esso è costruito da vari developers, viene anche stabilita una grandezza fissa dei tab (io per esempio preferisco il tab a tre spazi) per evitare confusioni od errori in fase di debugging. La tecnica principale prevede che si parta da sinistra verso destra per creare questi livelli ma, in determinate occasioni, il sistema può essere anche invertito e fatto partire da destra – ed in questo caso, si decide una lunghezza fissa per il livello base e poi si cancella un tab per passare a quello successivo.

LEGANERD 041519

Commentare! Il commento è una delle parti del codice più importanti in assoluto, poiché permette al maintainer, o allo stesso autore del codice, di conoscerne i punti più complessi ed oscuri e di poter capire il come ed il perché lavora in un determinato modo e situazione. Essi, per ora, non vengono letti dal compilatore e servono esclusivamente agli esseri umani per poter lavorare più o meno serenamente senza essere afflitti da improbabili algoritmi ricorsivi che di standard non hanno neanche una lettera. Anche i commenti vengono impostati secondo una tecnica: se per esempio ci troviamo davanti ad il codice classe = StudentiMaschi + StudentiFemmine, il commento non deve essere un inutile classe=studentimaschi+studentifemmina od un superfluo la classe è composta da studenti maschi e studenti femmina, bensì potrebbe essere la definizione di classe è situata all’interno del file scuola.h, in modo tale da fornire un ulteriore informazione che potrebbe essere essenziale in una fase successiva.

-Utilizzare un sistema di notazione standard. Ultimo ma non meno importante requisito per diventare un piccolo programmatore è la conoscenza dei più frequenti modi di nominare variabili, funzioni, constanti, etc. per avere una visione generale del programma più chiara a tutti i membri del team. Ne esistono vari ma, generalmente, esso viene stabilito democraticamente dal team o dittatorialmente dal capogruppo e quindi può variare in infiniti modi. Possono essere citate la notazione ungara, la convenzione CamelCase e la notazione posizionale.

Sfortunatamente, queste convenzioni non vengono sempre rispettate e sono molti i casi di programmi illeggibili a causa di programmatori malvagi persino all’interno del movimento Open Source, il quale da sempre ha l’obiettivo di rendere la vita dei debugger più semplice facendo in modo che tutti i programmatori imparino per una volta ad essere rispettosi dei propri colleghi.

Approfondimenti
Debugging
Bug
Notazione ungara
Ciclo di vita del software
Lista di debugger (software) famosi
Precedenti articoli di Propa
[more]Anche questo è venuto lunghetto. Ci sarebbe TANTISSIMO da approfondire, ma questo non è davvero il luogo adatto (e sono sicuro che ci siano utenti, i sopraddetti “veri programmatori” molto più preparati di me). Per il resto enjoy! Per la prossima settimana sono sinceramente un po’ combattuto: Editor, WYSIWYG e IDE o descrizione di un linguaccio di programmazione? Mmm…[/more]

Uber-paradigmi di programmazione
Alla ricerca del compilatore perduto
Basi ed acidi dei linguaggi di programmazione
Storia dei Linguaggi di Programmazione
Linguaggi di programmazione: parliamone!
Linguaggi di programmazione: parliamone!