Ho di recente lavorato ad un baco software che mi ha fatto riflettere sugli eventi eccezionali…ma che poi così eccezionali non sono.
No, non sto parlando degli eventi climatici estremi che purtroppo a causa del riscaldamento globale stanno diventando la norma anziché l’eccezione. Mi riferisco proprio alle Exception nel mondo della programmazione di software.
Sulle eccezioni ci sarebbe un sacco da dire e le discussioni non si contano: eccezioni sì, eccezioni no, “checked v.s. unchecked exception”, ecc.
Scopo di questo blogpost non sarà quello di addentrarsi in una lunga discussione, ma solo presentarvi un caso che mi è capitato lavorando con ZooKeeper, corredandolo con una mia riflessione.
Buona lettura.
Il contesto
Recentemente per un progetto ho usato ZooKeeper per implementare dei lock su alcune risorse (per una molto sommaria panoramica potete leggere questo articolo, tratto da una mia presentazione interna).
Brevemente, ZooKeeper gestisce una struttura ad albero simile ad un file system, condivisa in un ambiente distribuito. Può essere usato per implementare funzionalità come la “leader election”, le code o appunto i lock.
Nel nostro caso abbiamo creato un oggetto lock che richiama la libreria di ZooKeeper, dove un lock è rappresentato da un nodo. Per creare o rimuovere un lock basta richiamare la libreria per aggiungere o rimuovere un nodo (ci sarebbe da dire altro, ma andrei fuori tema).
Eccezione!
Quando si rimuove un nodo, ma il nodo non è presente, la libreria lancia un’eccezione. Questo comportamento sembra ragionevole e non avendoci fatto particolare attenzione, è risultato che anche quando si rimuove il lock di una risorsa non “lockata” (“non occupata” per i non addetti ai lavori) viene lanciata un’eccezione.
Nell’usare i lock però si è presentata una situazione non prevista.
In pratica nel ramo finally
del codice di una procedura che utilizza dei lock ci si assicura che i lock vengano tutti rilasciati. Già, e se un lock per vari e legittimi motivi non fosse stato acquisito? Eccezione!
La mia rifless”eccezione”
Questo episodio mi ha fatto tornare in mente quanto letto in un interessante libro, “A Philosophy of Software Design” di John Ousterhout, dove appunto viene citato un caso simile: un’eccezione lanciata nel metodo unset
di Tcl qualora la variabile non esiste.
Con l’autore ci chiediamo: “E’ veramente necessario lanciare un’eccezione se si vuole rimuovere qualcosa che già non c’è?”
Da un punto di vista imperativo, se ti dico di rimuovere qualcosa che in realtà non c’è parrebbe proprio un errore.
Vedendola da un punto di vista dichiarativo invece, quello che in realtà voglio è che sia verificata una condizione ovvero che ci si accerti che l’oggetto non ci sia. Tanto meglio se l’oggetto non c’era già prima.
Questo era proprio il nostro caso, dove si voleva appunto essere certi che non ci fossero più risorse “lockate”, senza porsi il problema se già non lo fossero.
Quello imperativo e quello dichiarativo sono due approcci molto diversi, e se quello imperativo sembra dare più controllo, in realtà si scontra con una maggiore complessità e difficoltà a tener conto di tutti i casi possibili; il controllo è quindi spesso illusorio, con il risultato di avere un sistema bizzoso e dal comportamento erratico.
In generale le eccezioni aumentano considerevolmente la complessità di comprensione e di utilizzo di una interfaccia. Implementare una logica che lancia molte eccezioni vuol dire demandare ad altri l’ingrato compito di capire come gestire gli errori e come recuperare la situazione. Se però noi non sappiamo o non abbiamo voglia di gestire il problema è probabile che chi riceva l’eccezione abbia ancor meno idea di come debba essere gestita.
Concludendo…
Il mio consiglio quindi è
Prima di lanciare un’eccezione, valutate bene se è effettivamente e chiaramente un errore, che non sia possibile gestirlo nel contesto, che sia effettivamente un caso eccezionale e soprattutto che sia chiaro al chiamante in quali condizioni viene lanciata.
Le eccezioni devono essere eccezionali, non nel senso che sono una “figata”, ma nel senso che devono essere rare come “sbattere il mignolino nello spigolo del comodino” (il dolore molte volte è lo stesso).