L’importanza dell’ambiente di sviluppo

Aprile 25, 2008

Questo titolo altisonante e’ il preambolo di una considerazione estremamente semplice :

Niente come un ufficio con un frigo pieno di birre e una quantita’ spropositata di tacos e salsetta piccante rende il venerdi’ pomeriggio produttivo.

600 righe rifattorizzate in 4 pomodori!!

Ora basta che il tutto riesca a integrare, ma lo spirito alcolico mi porta alla fede.


Nel segreto dell’urna elettorale

Aprile 15, 2008

Silvio ti vede, Walter no!

Ho pensato di commemorare i recenti risultati elettorali con questo adattamento di un celebre slogan da un’altra epoca. Ma sara’ poi davvero un’altra epoca?

Nella mestizia per il risultato generale ci sono pero’ spruzzi di estasi grazie alla lista degli esclusi.

Ora il grido dev’essere uno solo : “Tenete ben chiuse quelle finestre!”

Questa mattina commentavo placidamente che da ieri potrebbero anche ridurmi lo stipendio. Di colpo il fatto di non vivere in Italia mi fa percepire un immediato valore aggiunto.


Abstractions

Aprile 9, 2008

As usual, since I’m commenting something that was written in English I post in English.

I have got a solid respect for Joel Spolsky, he certainly is a professional who knows what a programmer is and how to explain it in clear, strong, words. He is not a paramount for me though and I would go as far as saying that in his posts his chief weapon is rounding up to the biggEST integer (and yes, this is kind of a monty python’s quote).

This often forces me to mentally prune and edit his posts in real time to avoid getting bad feelings while reading stuff that would otherwise have good value to me. So, let me say I dislike his style despite the fact that I reckon he is a great communicator.

One of Joel’s most famous statements is the law of broken abstractions. It is of course a very controversial statement and its conclusions seem to deny the value of abstraction itself.

I believe neither is true.

It is not a controversial statement as it is just a rephrasing of basic and well-accepted software engineering concepts : an abstraction is a simplification of reality created to more easily address a specific facet of a problem.

It does not deny the value of abstraction either.

Abstractions do have contracts, thus conditions for them to act accordingly to their contracts. A failing abstraction is still an abstraction, it is telling you that the conditions for it to work are not met, which narrows down the issue. A huge value.

I came to think once more about the law of leaky abstraction due to a weird issue with a wi-fi printer I had bought and installed at my parents’ home.

The printer is an HP C4380. Somehow it worked well enough with USB, but when used through wifi it printed only the first 2 lines of the first page. Incidentally, the printer’s own installation software had reported inability to connect to the printer, but I was able, for instance, to query the printer for ink levels, status and other data.

I’ll spare you the details, but after three hours I found out why the print command was failing (which I could state as : “I can obtain a paper-based copy of the document I got on the screen by asking the printer to print it”).

It was because this fancy printer has an MTU of 1466 bytes.

Such genius in the creation of a printer is astonishing, but, nonetheless, by taking a step beyond the basic abstraction, and then more layers, such as WIFI, then TCP, then IP, then Ethernet I finally restored the failing condition : “The sender must use transfer units of equal or smaller size of the receiver”.

Was the abstraction leaky? No, it just required that the underlaying abstractions had their conditions met, which in turn involved more conditions and further layers to have their own conditions met as well.

Returning to Joel’s excellent TCP-IP example: even if mice chew the cable in half my abstraction of TCP will not “leak”, it will return an error, which perfectly within the definition of the TCP abstraction. The error will be properly abstracted, mind you : just a failure, not, for instance, a list of packets whose retransmission failed.

This is Joel’s “upper approximation”: he purposefully presents the fact that abstractions have conditions for their validity in a way that makes one believe the whole idea of abstraction is moot. He does not say so, mind you, but so it sounds; the world “leak” is picked on purpose and this is why the law attracted so much attention.

How to manage a failing abstraction? Joel uses the necessity of inner-protocol knowledge to prove that indeed one cannot fully rely on abstractions. But they were never supposed to be fully reliable, if so, they would not abstract away any detail.

This is very clear if one delves in long-lost memories : “If we presume the angle of incidence is small enough, we can apply first-order gaussian formulae to lens-focus calculation”. That’s an abstraction, a model for reality, that simplifies a given aspect of the problem by providing a condition to the application of the abstraction.

It is not very different from saying : “If there exist a live routed path to a given destination (add here a lot of other conditions) TCP will guarantee delivery”

Why do we use abstractions to model reality since they are not almighty and will work only if the conditions are right? We ultimately do so to split concerns among different people. By letting some people take care that the conditions for a given abstraction to work consistently we can assign some other people to the task of using that abstraction. This two acts (taking care the conditions are met and using the abstraction) can then be geographically-spaced and time-spaced.

What if the abstraction stops working? Then someone should restore the conditions for it to work, if that someone is your own people then you might be able to fix it sooner than later, which is better, of course. But this doesn’t mean that you need to have people for recreating all of the required conditions.

As long as you can presume such conditions are maintained by the system in the long term you can define them as “reliable infrastructure”. Which just simply means : “I will spare with learning how to restore its nominal conditions for the sake of devolving more resources to using it instead”.

You don’t need to have Java programmers who are also Electricians, nor Bakers. But since you are presumed to create your own networking infrastructure, you likely prefer to have programmers who know about what an MTU is. If you can presume your system networking as reliable infrastructure, you can spare with that requirement for your programmers.

One hardly presumes that the abstraction “I can obtain bread by using money” will ever break. But if it does, it is not because “abstractions are leaky”, but because abstractions do have conditions. The reliability of a system is the chance such conditions might fail.

(Later addition, forgot to add it at first)

My parents didn’t presume they would need a trained technician to print a document, since they got one at hand they could have their system meet the required abstraction conditions (changing the MTU of the pc to be smaller than the printer’s), but they could have stayed at their own abstraction layer by following the suggested solution which was included in the manual (error responses) return the printer and pick a new one.

Then again, this is hardly a surprise for anyone. To be forced to accurately ponder which layers to consider as fully reliable and adequate for a given project is a mainstay of technical and general planning.


Il cambio delle lampadine

Aprile 5, 2008

Una metafora sul codice, che ne riprende mille altre simili, e’ tornata alla mia attenzione negli ultimi giorni :

“Devi cambiare una linea di codice, sai gia’ qual’e', e’ come sostituire una lampadina!”

Non ero coinvolto nella discussione e non ho commentato subito, ma in questi giorni ci ho riflettuto molto.

Credo che questa, e altre metafore dello stesso tipo, nascano dalla limitatezza espressiva del codice nel descrivere le proprie interconnessioni.

Se ci si limita a un’occhiata, la linea di codice “sembra” isolata, come sono isolate le voci di un elenco telefonico. Questa carenza espressiva nel codice permette di creare metafore del tutto fuorvianti, ma dal forte appeal.

Cosi’ come il codice, progetto della macchina che il compilatore costruira’, lo schema elettrico di un edificio, progetto della successiva installazione concreta del sistema elettrico, espone dei punti in cui e’ possibile effettuare una semplice sostituzione e aspettarsi di mantenere la coerenza del resto del sistema.

L’attacco di una lampadina e’ proprio uno di questi punti.

Altri punti dello schema elettrico invece non condivideranno questa qualita’ : come non citare l’esempio della ventola del mio bagno, che ha cominciato a funzionare solo dopo aver attaccato i fili della lampadina del bagno, perche’ proprio da questi alimentata?

La metafora del cambio della lampadina per descrivere la modifica di una linea di codice e’ corretta solo se quella linea di codice condivide le caratteristiche della lampadina : un oggetto che incapsula completamente le funzionalita’ desiderate (produrre luce) con un’interfaccia (l’attacco, il consumo, la polarita’) che ne astrae i dettagli implementativi.

Non a caso, quando nel codice si ci trova a rispettare tali proprieta’, sostituire una linea e’ semplice proprio come cambiare una lampadina.

Queste proprieta’ pero’ non sono comuni: tipicamente le linee di codice che le rispecchiano sono quelle in cui avviene l’istanziazione di una classe concreta dentro ad una variabile astratta (supposto che l’implementazione rispetti pienamente il contratto dell’astrazione).

Ecco, linee di questo tipo sono rapidamente sostituibili :

CalcolatoreDelMutuo calcolatore = new CalcolatoreMutuoQuinquennaleBancaIbs();

con altre lampadine :

CalcolatoreDelMutuo calcolatore = new CalcolatoreMutuoDecennaleBancaOpl();

Da qui nasce il valore della composizione : moltiplica le linee di questo tipo.

Sfortunatamente nel classico sistema di media-alta complessita’, dal design, diciamo, non perfetto, ci sono pochissimi punti di astrazione con il necessario grado di disaccoppiamento e sostituibilita’. Cosi’ pochi da rendere statisticamente improbabile che una linea da modificare ricada tra quelle fortunate.

La maggior parte sono linee che non rappresentano punti di aggancio di lampadine, quanto piuttosto fili di interconnessione, che su uno schema elettrico sono proprio rappresentati da linee, giunzioni e altri elementi non modulari.

Cambiare quindi una linea di codice corrisponde piu’ comunemente a cambiare un filamento a incandescenza che corre da una stanza all’altra e finisce dritto nelle prese a muro di due stanze diverse.

Visto che il filamento non e’ mai uguale agli altri ed essendo in serie a tutto il circuito, cambiarlo corrisponde a modificare lo schema elettrico del sistema.

Se ogni linea di codice ci sapesse mostrare le sue interconnessioni a monte e a valle sarebbe un gran guazzabuglio, ma sicuramente non nascerebbe l’idea di poterla “sostituire”. Infatti non diciamo “Facile come creare una derivazione al terzo piano”. Lo schema elettrico e’ molto chiaro nel definire cio’ che e’ modulare ed astratto da cio’ che e’ particolare, concreto e quindi fragile.

La sostituibilita’ e’ una caratteristica rara che richiede accurata levigatura per poterla raggiungere e anche cosi’, sempre e solo nel senso di definizione dell’astrazione. Non diciamo certo “facile come cambiare una lampadina con un asciuga capelli”.

Quindi, volendo proprio trovare una metafora elettrica per la modifica di un software fortemente interconnesso potremmo fare cosi’:

1000 linee di codice sono lo schema elettrico di un piano di un palazzo e ogni linea tracciata sul piano puo’ collegarsi a una delle linee di uno qualunque degli altri piani.

Dunque quando si pensa di cambiare una linea di codice nel classico sistema legacy, la metafora corretta non e’ il cambio di una lampadina, piuttosto e’ “cambiare lo schema elettrico cosi’ che la’ dove adesso c’e’ un cavo che corre tra i piani 15,16 e 289 ci sia invece una resistenza di 5 ohm tra i piani 15 e 289. Il palazzo ha 300 piani in cui l’illuminazione e’ data da filamenti a incandescenza che corrono sospesi tra una stanza e l’altra, tutti simili, ma a loro modo diversi e quindi non intercambiabili, inoltre c’è una lavatrice in soffitta che si accende solo se premi un pulsante in cantina e altri tre pulsanti sconosciuti che incidentalmente fanno anche altre cose, oltre a garantire il funzionamento della lavatrice”.

Naturalmente per il palazzo la lavatrice e’ mission-critical.

Descritta cosi’ i costi di modifica diventano di colpo comprensibili. Non si tratta di giustificarli, quanto piuttosto ammettere il problema.

E’ subito evidente che il problema e’ lo schema elettrico e che si deve lavorare sul definire degli attacchi per lampadine, loro si’, sostituibili.

Ogni giorno si lavora per aggiungere una lavatrice in giardino e un ferro da stiro nella hall, ma con pari priorita’ si deve anche evitare in qualche modo di essere costretti ad alimentarli con un pannello solare nello sgabuzzino illuminato coi filamenti a incandescenza del pianerottolo di sotto.

Il vero guaio, a livello di comunicazione tra persone di ambiti diversi, e’ che certe metafore, valide per sistemi fortemente ingegnerizzati, trovano riscontro in quasi tutta la tecnologia che ci circonda e quindi hanno una componente di innegabilita’ che contagia anche coloro che sanno perfettamente cosa significhi davvero cambiare una linea di codice. Figuriamoci qualcuno il cui principale, e comprensibile, interesse e’ guadagnare il pane mettendo un’altra lavatrice in giardino.

Per il committente l’immagine di associare alle linee di codice altrettanti punti di sostituibilita’ ha una forza data dall’immediatezza delle proprie esperienze in contesti diversi.

Guardandoci attorno troviamo migliaia di esempi di sostituibilita’ bella e pronta : il cibo, le lampadine, le lavatrici, i vestiti.

Se la nostra esperienza quotidiana fosse invece quella di usare prototipi di microprocessori e dover cambiare la loro struttura per fargli smettere di moltiplicare e iniziare a sommare i grammi della nostra bilancia, assoceremmo le linee di interconnessione alle linee di codice, e i moduli intercambiabili delle memorie alle astrazioni definite nel codice.

Credo che da qui nasca l’esasperazione e l’idea che il software aziendale sia per sua natura un macello: la mancanza di vicinanza “estetica” tra una linea di codice e cio’ che e’ realmente : un dettaglio concreto dello schema progettuale di un sistema complesso, fa pensare che cio’ che abbiamo in mano sia facilmente malleabile. Una volta subìto l’ennesimo smacco a questo ordine di idee inizia a nascere ogni sorta di ritrosìa nel guardare il problema da piu’ vicino e a darlo invece per insolubile.

Come andare a spiegare a un non-tecnico che non siamo ancora stati in grado di trasformare la nostra linea di codice in un prodotto ingegnerizzato, controllato e sostituibile? Come spiegare che un tale lavoro ha un costo pari alla moltiplicazione per 5 del costo iniziale di creazione del prototipo?

Spesso, piuttosto che questa ammissione di debolezza, la forza di una metafora “di per se’ evidente” come quella della lampadina, fornisce il necessario momento di escapismo di cui ogni tecnico responsabile ha prima o poi bisogno : il committente ha le sue buone ragioni! Cambiamo questa lampadina!

Quello che conta e’, dopo aver sbottato, tornare a lavorare sul creare gli attacchi e le lampadine, ricordandosi il premio che ci aspetta: una volta che le lampadine sono state create, a un programmatore non ci vuol niente a cambiarne una o cento, proprio come ci aspetteremmo da un buon omino di casa.


Piccole novità

Marzo 2, 2008

- Non vivo più in Italia

- Vivo invece in Svizzera, in Ticino

- Non sono più dipendente di una società di consulenza

- Al contrario lavoro in una software house

- Sono stato per un mese senza internet

- E’ stato orribile

- Lavoro in un team XP!


Bells and principles in iterative development

Gennaio 3, 2008

A few days ago a fellow reader of the XP list posted this problem : he was test driving some functionality and he found that he kept on changing his own tests. This striked him as a potential issue, as it could be an infringement of the Open Close Principle.

This brought the focus on the role of OCP for detection of the need for refactoring in iterative development.

As a result to my response to that post I was later asked where one could read more of my thoughts regarding the use of OCP as a guide for identifying code smells.

Since I usually write this blog in Italian and since most of my thoughts on this topic are bound to slides of past seminars, I’ve devolved a few hours since yesterday to coalesce my thoughts from minnenratta and elsewhere regarding the guiding role of principles while evolving the design of code.

In the post I said that the third time I happen to change the code in a base class I will worry why I am changing it and that noticing the opposite, that one is changing less and less code in order to obtain a desired behavior, is a hint that OCP is being followed.

I think that OCP, like other design principles should never be used up-front in evolutionary development, but only as a tool for improving a design that has already proved a limit and thus, is leading to degradation. To prove that a design is limited your code should show degradation.

For instance I believe that the Single Responsibility Principle should be thought of as Single Variation Axis Principle instead , as a Responsibility is associated and identified with a type’s interface method. This leads to applying it to any disjunct logic of a class before any bell has rung and no need for a further axis of variation has appeared.

To tip the hat to fractals I could restate this to show the simmetry with the main iteration that led me to creating degradation in the first place : Code degradation is the red bar of my design. First I need the red bar then I implement the design that solves it.

Back to the matter of base class modification. I am not sure that it is OCP that highlights it in my mind as a degradation. At that point I’m still realizing that something is indeed a smell. OCP does come into play later on, when the hint transforms in an identified smell and I am trying to understand what caused it and thus what I should do to clean it (and thus pick the right refactoring moves).

I would define a “hint” as something odd that I notice by difference from experience and training, something which might be a smell.

“Oh… why am I touching this again?”.

Why am I asking myself that question? Right now I would bet it’s not because of my knowledge of OCP, it should never be.

This is very likely the source of the original poster paradox with OCP. He was both envisioning up-front design by strategically closing his code, and he was using OO principles such as OCP or SRP as first-level hints. In that context they are far too strong and often misleading.

That said, I believe that the sensibility to changing code in base classes should rather spring out from a broader, weaker heuristic : “good base classes should have few reasons for change and many reasons not to change”. This heuristic is of course motivated through the classic consideration that every dependence towards a class is a good reason for that class not to change, while accounting for the fact that extensions are very strong dependencies.

Copy paste rings another bell, in the first pass I notice that there’s copy paste, I might be at the beginning of a combinatorial explosion and SRP would lead me to understand there are two axes of variation evolving and that I should move inheritance to composition; but the bell rang in the first place thanks to a lot of excellent points (DRY is a good grouping of them) and the list of known smells, so instead of proceeding with the next test I ask myself : “Why did I copy-paste this?”.

As I said in my post to the XP list, it certainly draws my attention the act of changing a given class for the third time. Yet there are lots of reasons to do that, first the heuristic will attract my attention to what I’m doing, then design principles will let me find out the real, design-level cause, while agile (manifesto) principles will help when it’s not due to design at all, along with a plethora of other principles which all have gathered into common sense.

A somehow more concrete example to sum this all up.

Let’s suppose I am writing some solution code and I’m changing a class which is a the root of a hierarchy.

I know that there should be preciously few reasons for this class to change, yet it is changing.

Suddendly I stop and say :

Why am I changing this class once more?

I might have a real issue in my hands, so I note it and finish the change. While refactoring, thanks to that hint I’ll stop on that code and ponder. More questions might arise, depending on what I see and what “second-level” oddities I notice, here are some examples of the analysis that might follow.

Case 1 : I am adding yet another “if” to my method. Definitely a conditional logic smell. “Am I performing extension through modification? Should I think about a hierarchy instead?”. OCP is talking here. This smell might still be the result of a different issue yet, I might be defending against a specific behavior of a type I am invoking [if(foo instanceof Gas) foo.compress())]. “Am I suffering lack of substitutability?”, Liskov would tell me, but then I might notice that I failed to properly mantain the abstraction because the interface is too complex, and I would notice this because I know that not well-segregated interfaces produce this sort of abstraction leaks.

Case 2 : It just happens that out of three behavior changes requested by the customer all of the three do fall in this class, in three different methods. “Has this class too many reasons to change?” SRP would produce this second level question.

Case 3 : I changed Class A and in this class I use A, along with B and C, which were the cause of the previous changes. “Is this class implementation coupled with other concrete implementations I should abstract?”. Dependency inversion is producing the question this time.

Case 4 : I have a template method structure in place between the base class and its extensions and a genuine need by the customer to update the desired high-level behavior which is, by all rights, located in the base class. None of the design principles here is ringing any bell for second-level questions. The best way to mantain coherence is to indeed update the logic in the base class (trying to override high-level behavior from a subclass in a template method might result in substitution inconsistencies) , so I’ll mantain this simple, but so far adequate pattern, and address the real issue, which does not seem related to design.
I ask myself “Do I communicate well enough with the customer?” Am I helping him properly define what he wants? Is he still trying to find what he wants?”. These all come from valuing communication, if I had different principles in this context I might instead ask myself “Is the specification clear enough?” and get to very different solutions, how sad…


Rivelazioni dagli anni settanta

Dicembre 30, 2007

(Questo post volevo scriverlo mesi fa, finalmente ho avuto il tempo di concluderlo)

Bazzicando l’ambiente del design object oriented, dell’ingegneria del software e dei metodi agili avevo spesso sentito citare The Mythical Man-Month, di Brooks, ma avevo sempre posticipato la sua lettura avendo scoperto che si tratta di un libro scritto trent’anni fa e che per lo più tratta di project management.

Come poteva un libro così datato avere qualcosa da dirmi che non fosse stato raffinato in questi trentanni in altri testi o che non fosse diventato spettacolosamente obsoleto?

Per chi non fosse del settore : per avere una sensazione della velocità con cui lavora l’obsolescenza nell’IT bisogna moltiplicare per 5 l’età di qualunque pezzo di informazione. Ad esempio, leggere oggi un saggio del 2005 che tratti di informatica è come per un medico leggere un saggio di dieci anni fa, del 1997. Un libro scritto nel 1995 invece dà tipicamente la stessa sensazione che avrebbe un ingegnere civile a leggere un libro di costruzione di viadotti di sessanta anni fa.

Davvero.

Sono persino conservativo in questa proporzione : se si pensa che molti testi di medicina, ingegneria civile, meccanica, aeronautica di sessanta anni fa hanno ancora un loro valore e utilità, mentre i libri di informatica di dodici anni fa che hanno ancora qualcosa da dire oggi si contano sulle dita di due mani, si potrebbe portare il moltiplicatore a 10.

Tornando a The Mythical Man-Month.

Questo libro ha trent’anni, quindi per altri settori sarebbe equivalente a un libro di 150 o anche 300 anni fa. Insomma, come se un chimico trovasse che la termodinamica di Sadi Carnot contenga per lui dei concetti innovativi.

Eppure questo libro di un’era passata asserisce concetti pertinenti la gestione dei progetti software complessi che, ad oggi, sono in buona parte ignorati.

Non voglio dire che il libro non abbia avuto audience. Al contrario, la sua fama è inequivocabile, ma questa fama evidentemente non ha influenzato proporzionalmente l’industria.

Questo libro è esistito ed è stato letto durante tutta la mania costruzionistica e predittivistica degli ultimi decenni, eppure frasi come “aggiungere forza lavoro a un progetto software in ritardo ne aumenta ulteriormente il ritardo” o capitoli come “il team chirurgico” che descrive come la massima priorità in un progetto software sia mantenere la coerenza concettuale del sistema sono stati largamente ignorati durante l’affondamento reiterato di progetto dopo progetto per cause perfettamente conosciute e continuando a spingere su processi che ignorano, se non acuiscono, queste ultime.
Addirittura la modularità (e quindi il disaccoppiamento) è stata presa a bandiera dell’approccio predittivo, mentre nel libro essa è strumentale alla possibilità di testare e far funzionare il sistema il prima possibile. La dichiarazione seminale del libro “It is a very humbling experience to make a multi-million dollar mistake” pone le basi per la successiva discussione dell’importanza di formare e coltivare le capacità di design dei propri tecnici, eppure questo è stato ignorato di fronte alle campane della sostituibilità delle persone.

L’attualità del libro è solo uno dei suoi meriti. Non solo è attuale, è anche estremamente netto. Brooks aveva le idee molto chiare e il dono di scriverle con eccezionale limpidezza. Il libro è ricolmo di frasi eleganti, forti e ben incapsulate, rendendole memorabili.

In fine queste frasi memorabili sono anche profetiche: descrivono con infinita chiarezza dolorosi errori di management dal costo esorbitante già sperimentati, ma che sarebbero stati ripetuti negli anni successivi e continuano, inesorabili, a essere ripetuti oggi.

Non posso nascondere un profondo fastidio riflettendo su questo libro. La mia esclamazione più frequente è stata : “Ma allora ve l’avevano detto! Lo sapevate!”.

Eppure ancora oggi si vedono gantt calcolati e aggiornati usando la proporzionalità inversa sul numero degli sviluppatori coinvolti, sottostimando il valore e il costo esplosivo della comunicazione all’interno di un progetto (cosa che è ancor oggi difficile da far accettare!).

Nella mia edizione è stato incluso un altro saggio di Brooks, più tardo di quelli che componevano l’originale MMM, ma altrettanto magistrale. “No Silver Bullet” prosegue infallibilmente la tradizione dei suoi predecessori nello sfatare i facili entusiasmi dell’industrializzazione del processo di sviluppo del software sulla base di un’intelligente distinzione nelle complessità che si affrontano nel mestiere : la complessità può nascere da cause essenziali e da cause incidentali.

Brooks spiega come, nella creazione di un sistema, si affronti una complessità incidentale, generata dagli “accidenti” della pratica, come ad esempio l’espressività (limitata) del linguaggio di programmazione, il ritardo con cui si ottiene il feedback del proprio lavoro o l’ambiguità dei messaggi di errore imprevisti. Allo stesso tempo si affronta una complessità essenziale, causata quindi dalla complessità intrinseca del problema da risolvere, insomma, da quelle che oggi chiameremmo business rules, ma non solo, la complessità essenziale nasce anche dalla varietà delle visualizzazioni richieste, dall’intrecciarsi delle politiche di persistenza dei dati dettate dal business, insomma, tutto ciò che, a prescindere dalla tecnica applicata alla soluzione, rende multisfaccettato il problema.

La conclusione di Brooks è elegante, come il resto del suo pensiero : pur ammettendo che la complessità essenziale sia tanto piccola da essere solo un decimo della complessità incidentale non vi sono all’orizzonte soluzioni che portino alla riduzione della prima, ma esclusivamente tecniche di gestione di singoli aspetti della seconda. Se anche tutte queste tecniche avessero avuto pieno effetto si sarebbe ottenuto un aumento di produttività di un fattore dieci e non oltre, di gran lunga inferiore a quanto era comune promettere ai tempi, perchè la complessità essenziale dei problemi fronteggiati da un progetto software non sarebbe stata intaccata da nessuna di esse.

Di fronte all’entusiasmo per l’esplosione delle capacità di calcolo dei computer è comprensibile che MMM, e più tardi “No Silver Bullet”, possano essere stati accusati di pessimismo, ma è imperdonabile che il primo e ancor più il secondo non siano diventati progressivamente il vangelo del management IT quando le loro previsioni si sono confermate perfettamente lucide e realistiche, piuttosto che pessimistiche.

Djikstra l’aveva detto in modo forse criptico quando aveva affermato che al crescere della potenza dei calcolatori il “problema” del software sarebbe esploso di pari passo, ma leggendo la prosa di Brooks non ci può essere giustificazione, la nettezza del suo pensiero rende la natura della sfida inequivocabile.

Equivocare ancora un messaggio così chiaro e continuare a promettere e ad accettare promesse di spettacolari incrementi di produttività tramite l’industrializzazione della programmazione, il più astratto degli sforzi progettuali della moderna tecnologia, non può più essere addotto a un’incomprensione avvenuta in buona fede : qualcuno, molti in verità, si sono tappati gli occhi e le orecchie per molto tempo e non hanno ancora smesso.


Come trasformare Safari in una distopia orwelliana

Ottobre 21, 2007

Safari è un servizio bellissimo, non certo economico, ma O’Reilly comprensibilmente non può rendere troppo economica la possibilità di leggere centinaia di libri tecnici (e sappiamo bene quello che costano) comodamente online.

Detto questo posso vantarmi di aver l’anno scorso proposto e in fine ottenuto l’accesso a Safari illimitato per tutti i dipendenti della mia società.

Ho sempre voluto provarlo e, se posso permettermi la auto-pacca sulla spalla, è stato un colpo da maestro : ora posso leggere tutti i libri che voglio senza sborsare quantità smisurate di soldi ogni tre mesi (continuo a comprare il cartaceo ogni tanto, per quei libri che trovo capolavori e che mi piace rileggere).

Ovviamente c’è un però : gli accounts a nostra disposizione sono di tipo illimitato, parecchio costosi, e quindi non c’è un account per ogni dipendente.

Di fatto sono stati acquistati un certo numero di accounts (penso una ventina) che fungono da pool da cui, a ogni login da parte di un dipendente, ne viene estratto uno. Se troppa gente si connette a Safari contemporaneamente ovviamente si esauriscono i venti accounts e si rimane a bocca asciutta.

E’ indicativo il fatto che su migliaia di dipendenti io non abbia mai avuto problemi ad accedere… ma sorvoliamo.

Questo sistema a pool ha un chiaro difetto : i libri che uno ha consultato rimangono certamente nella lista dei preferiti dell’utente, ma sfortunatamente la prossima volta che mi connetterò ho solo una possibilità su venti di cascare di nuovo sullo stesso utente.

Ho anche il sospetto che l’assegnazione degli account non sia perfettamente randomica, in particolare direi che è maliziosamente randomica.

Io ad esempio tendo a finire frequentemente su un utente che ha i suoi preferiti per lo più ricolmi di tomi sui DB, in particolare MS SQL Server. Che ironia. Ci dev’essere stato un DBA che un giorno ci ha proprio dato dentro, oppure che è stato molto fortunato ed è ricarduto giorno dopo giorno sullo stesso utente.

Immagino il suo stupore quando la sua fortuna si è esaurita ed è cascato sull’utente che avevo pescato io il giorno prima : “Ma chi sono ’sti pragmatic programmers?”, “TDD chi?!”

Ciò che mi ha spinto a raccontare la cosa è però il feeling surreale, molto 1984, che viene a crearsi con questo sistema del pool. Si perchè chiunque abbia creato quegli utenti ha in sè, nascosto da qualche parte, il germe del burocrate nazista.

Chiunque avrebbe colto l’occasione per dare dei nomi interessanti a quei venti account, creando una sorpresa ai fruitori che, giorno dopo giorno, avrebbero scoperto di essere l’utente “Holy Hand Grenade”, “Bond, James Bond”, “Unexpected Surgery”, “Catbert” e così via. Quantomeno, io, non me la sarei fatta sfuggire, sarò infantile.

La realtà dei fatti è invece questa.

“Buongiorno Safari! Vediamo… cosa potrei leggere di bello oggi?”

“Buongiorno UTENTE NUMERO 15, oggi potresti leggere un altro po’ di ‘SQL Server Forever’!”

Ma si può?

Non invidio chiunque sia stato. Non trovare un momento per inventarsi dei nomi che valesse la pena lanciare addosso a casaccio a centinaia di utilizzatori e scegliere invece di eseguire a mano una for k < 20 print ‘UTENTE k’ può solo significare un’appartenenza traversa alla stirpe di Goebbels, la completa alienazione dalla propria giornata lavorativa o un forte desiderio di farla finita e infilare la testa nel microonde. Un’opzione non esclude le altre.

L’unica speranza è che sia tutta una sottile citazione da “The Prisoner” mirata a satirizzare l’inquadramento del dipendente nella grande famiglia del villaggio corporativo, ma ho i miei dubbi.


Ultima versione di lazy-db

Ottobre 1, 2007

Avevo pensato di limarla un altro po’ prima di renderla pubblica, ma tant’è…

Ecco l’ultima versione di lazy-db, che è l’erede dei miei primi esperimenti di creazione dello schema di un db in tdd, come raccontato qui .

In questa versione :

  • ho aggiunto un po’ di compatibilità, per MySQL e per PostgresSQL
  • sono passato a hypersonic 2 come db in-memory
  • ho aggiunto la possibilità di definire delle chiavi primarie
  • una classe per facilitare l’interrogazione
  • il metodo “insert” dentro a Column per facilitare la popolazione del db in casi semplici

Restano poche linee di codice, e quindi fa davvero poco, però è comodo.

Trovate lo zip con il jar compilato e il pom di maven2 che descrive le dipendenze su questa pagina del jug torino.


Checked Exceptions, un’alternativa notturna

Luglio 31, 2007

Fino a pochi giorni fa sulla mailing list del jug Torino si discuteva dell’utilità delle checked exceptions.

Antonio le odia profondamente e in un certo senso anch’io, tra di noi resta un piccolo differenziale : io dico “sono così odiate perchè sono abusate, basterebbe che non fossero la scelta di default quando si parla di eccezioni” e lui dice “visto che dovrebbero essere usate di raro e invece vengono messe come il prezzemolo togliamole e basta”. Penso di poter dire che siamo daccordo sul problema, ma che proponiamo diverse strategie correttive, che non voglio discutere qui.

Questo post lo sto scrivendo perchè circa quaranta minuti fa erano le due di notte e stavo tentando di addormentarmi, erano già due ore che ci provavo rileggendomi il primo libro di Harry Potter e, a testimonianza del valore del testo, non era servito a nulla.

Ho chiuso il libro e spento la luce e ho cercato di pensare a qualcosa per rilassarmi. Strano a dirsi, ma mettermi a pensare alle discussioni sulle checked exceptions del jug non ha aiutato per niente e invece ho iniziato a formulare un’idea di cui al momento non mi azzardo a stimare il valore, vista l’ora e il mio oggettivo stato di dormiveglia, ma che prevedo che all’alba mi suonerà bislacca e scialba di contenuto.

Mi impegno comunque a metterla online domattina, se non altro per il suo valore di divertissement, non appena potrò permettermi di accendere il computer che è connesso a internet, cosa che, se mi azzardassi di farla adesso, scatenarebbe le giuste e funeste ire della mia dolce metà.

Passo quindi ad illustrare l’idea bislacca, premettendo ancora una cosa : non ho idea se quello che sto per descrivere è cosa già ben nota o se addirittura fa parte della tradizione, se lo è non servirà certo arrivare alla fine perchè suoni il campanello a chi già sa e quindi il danno sarà minimo, ad ogni modo le mie uniche scuse saranno la mia ignoranza e l’ora improponibile.

La cosa che veramente mi fa dare di testa con le checked exceptions, non solo quando si tratta di eccezioni che dovrebbero invece essere di runtime (e qui l’abuso di cui sopra), ma anche quando avrebbero il loro perchè, è che spronano alla duplicazione del codice.

Un try/catch è di per sè uno switch su due o più casi e se c’è una caratteristica degli switch/if è che non permettono il riuso del codice di gestione all’interno dei loro casi.

public void chiamante() {

try {

chiamato();

} catch (Exception e) {

}

}

Certo, al ripetersi di una condizione è possibile estrarre un metodo e richiamarlo ogni volta, ma, a meno di non abbarbicarsi con qualcosa di statico, il riuso finisce lì, spesso limitato a una singola classe, magari persino esteso a uno Strategy che gestisca gli errori in molti diversi oggetti, comunque di solito non si riesce a liberarsi dello switch in sè, si riusano solo i singoli casi, il riuso non è mai davvero trasparente.

Quante volte si vedono dei catch duplicati in mille punti diversi del codice e si ci sente nel bisogno di riconoscervi un concern trasversale che richiederebbe davvero la programmazione ad aspetti, pena il copia incolla e quindi l’esplosione della manutenzione.

Spesso sono proprio le eccezioni che non dovrebbero essere checked che generano la duplicazione più diffusa. Non c’è niente di più facile che copiare un printStackTrace o un bel log.error(e), e questi spesso indicano delle false checked : se non sappiamo come gestirla perchè mai dovremmo essere obbligati a farlo in questo modo che, alla meglio, introduce un bel fail-slow?

Questa notte stavo ripensando a questa duplicazione e l’ho collegata alla discussione sul “contratto” del metodo e di come questo sia ulteriromente specificato dalla presenza di un’eccezione checked o unchecked. Forse il motivo per cui questo post mi diverte tanto e non mi fa dormire è che nasce da una considerazione del tutto teorica, che non ha nulla di pratico in partenza, ma in cui trovo un deciso valore pratico.

Si può vedere un try/throws e quindi la dichiarazione di una checked exception da parte del metodo come la richiesta del rispetto di una debole PREcondizione : “per potermi chiamare devi fornire una try con la catch giusta o al massimo una throws”. Mentre nel caso delle eccezioni unchecked si tratta di una debole postcondizione “se succede qualcosa di imprevisto al mio interno ti avviso”.

Questa precondizione in Java è implementata con una sintassi che mira a rendere disponibile sia il contesto del chiamante che i contenuti del chiamato. A questo punto viene bene pensarla come una specie di closure (si, continuo a citare le closures, Ruby fa malissimo). Aldilà del suono fighetto della parola il maggior vantaggio che penso si abbia dal citare le closures è spostare la locazione dell’esecuzione dentro al metodo chiamato, non il chiamante.

A questo punto il passo è breve, la mancanza di closures in Java mi obbliga ad una soluzione che forse in questo caso preferisco addirittura alla closure : il vincolo di gestione della checked exception come parametro del metodo.

Non per niente l’implementazione classica di una precondizione (debole) è la richiesta di un certo input, quindi un parametro.

Tutta questa storia per arrivare qui, ve l’avevo detto che era scialbetta.

Però mi piace molto, ho passato un bel po’ di tempo a ricamarci sopra, al punto che ho deciso che l’unica cosa per smettere era buttarla giù e desistere dal tentare inutilmente di dormire. Più ci ricamo più mi piace.

Come implemento la mia eccezione? Con un parametro che richiede un ExceptionManager, o se preferite un ExceptionListener. Il sistema delle eccezioni in Java non è una Chain of Responsibility dove la catena è lo stack di chiamata? Quindi tanto vale chiamarlo ExceptionListener.

Quindi, se l’immediato contesto del chiamante sa come gestire un’eccezione passerà al chiamato un ExceptionListener locale, che, non a caso, potrà portarsi dietro parte del contesto, sotto forma di variabili d’istanza del listener, per gestire il caso eccezionale.

public void chiamante(

chiamato(new ExceptionListener() {

public void manage() {

…gestisci eccezione…

}

});

)

Là dove normalmente propagherei l’eccezione con una throws propago invece il parametro e lo aggiungo alla dichiarazione del chiamante :

public void chiamante(ExceptionListener listener) {

….

chiamato(listener);

….

}

Notare la rimozione dell’if (try/catch) che di fatto era solo una ripetizione di un if dentro al chiamato che lanciava l’eccezione. Non ho mai capito perchè, per gestire un errore in un diverso contesto, dovessimo prima dire dentro al chiamato “se succede questo sgancia E” e poi dentro al chiamante “se ti arriva E fai questo”, così diciamo direttamente “se succede questo FAI quest’altro” punto, dove “quest’altro” è un bel listener che ci arriva da chissà dove. Un if è morto, lunga vita all’if.

Invece di propagare la dichiarazione dell’eccezione alla compilazione che deve per forza essere accoppiata da una specifica gestione, anch’essa stabilita staticamente, propago solo la dichiarazione del parametro alla compilazione, mentre posso iniettare al runtime la gestione dell’eccezione. Nel caso in cui la gestione sia molto lontana dal contesto del chiamante stiamo sfruttando l’iniezione delle dipendenze e quindi non stiamo vincolando il chiamante a una gestione particolare con dettagli che non gli competono, niente dipendenza. Invece di riempirmi di try/catch posso configurare i miei bean di Spring con dei bean di gestione delle eccezioni! L’avevo detto che ci avevo ricamato sopra no?

In breve è spiccicato quello che si ottiene con la sintassi classica, se non che questo promuove il riuso e evita gli switch che invece promuovono il copia-incolla.

Un altro ricamo :

E’ vero che nel listener ci portiamo dietro quello che ci serve del contesto del chiamante per gestire il caso eccezionale del chiamato, ma non abbiamo a disposizione il contesto del chiamato, ad esempio in questo caso :

public void transferFile(ExceptionListener pingFailureListener) {

if(pinga(host)) {

sendFile(host,file);

} else {

//se non è disponibile

pingFailureListener.manage();

};

}

Vorrei magari avere a disposizione “host” al momento della gestione.

Non rinnego infatti il ruolo delle eccezioni in quanto contenitori del contesto del chiamato, segrego solo le checked a quell’unico ruolo, lasciando alla definizione dei parametri il compito di imporre le precondizioni.

Da questo punto di vista eviterei ormai di chiamarle eccezioni e di dargli un nome più significativo del ruolo effettivo :

else {

//se non è disponibile

pingFailureListener.manage(new UnavailableHost(host,repetitionsSoFar));

}

O, quando il dato utile è uno solo, semplicemente passando il dato in sè, soprattutto se altrimenti creeremmo solo un data holder :

else {

//se non è disponibile

pingFailureListener.manage(host);

}

Qui i generics possono aiutarci a documentare meglio la precondizione e a evitare dei cast, ad esempio se host è un URL :

public void execute(ExceptionListener < URL > pingFailureListener) {…

o nell’esempio precedente :

public void execute(ExceptionListener < UnavailableHost > pingFailureListener) {…

Sempre per la serie dei ricami, notate che è UnavailableHost, non HostUnavailable, proprio perchè l’istanza che passiamo non ha più sapore di “evento” come un’eccezione invece ha, ma solo di fornitore di contesto, “unavailable” caratterizza solo l’identità di host, non descrive un accadimento.

Ancora un ricamo :

Ma ci siamo persi lo stack! Non potrò facilmente scrivere un listener che stampi lo stack delle chiamate!

In fin dei conti però, i casi in cui una checked exception ha come soluzione “printa lo stack e tanti saluti” sono proprio quelli in cui si dovrebbe parlare piuttosto di una unchecked exception.

Più scrivo più penso che siano una serie di banalità, ho rifattorizzato uno switch con uno strategy in fin dei conti. Ma, se si tiene in conto anche il caso del throws, che si risolve semplicemente con la propagazione del parametro, ci trovo un’eleganza che mi piace. Non a caso sono ancora sveglissimo alle 4.30 che penso a come sarebbe bello se eccezione diventasse sinonimo di unchecked exception mentre a fianco a Object, Throwable, nell’olimpo delle radici di della gerarchia ci fosse anche l’interfaccia ExceptionListener.

4.32, ultimo ricamo, giuro!

Lista dei parametri lunga!! Abiura!!

Vero. allunghiamo la lista dei parametri, ma è solo perchè riconosciamo che le checked erano semplicemente un’altra lista di precondizioni, ed è la somma delle due dovrebbe essere corta. Almeno così non possiamo infilare la testa nella sabbia : “Ho tre parametri e tre checked exception, tutto ok!” ora abbiamo sei parametri ed è chiaro che stiamo scrivendo schifezze. Inoltre così possiamo rifattorizzare via i parametri di troppo con le stesse regole che usavamo già per i parametri “tradizionali” (i listener possono diventare variabili d’istanza no?).

In breve, in questo modo si ha il comportamento di una checked exception, ma si forzano le parti in causa a gestirle in quanto tali : recuperabili, casi di funzionamento eccezionale ma nominale e riusabili.

4.40, Domani la pagherò molto cara.

Aggiunta del giorno dopo :
Non che mi attenda chissà quali commenti a questo post, ma per chi stesse leggendo questo su LiveDigital sappiate che, a meno di non essere registrati su LiveDigital, non potete commentare. Me ne rammarico, hanno 80mila funzioni ma non permettono i commenti sui blog. Classico. Non l’ho scelto io e non posso cambiare così presto. La buona notizia è che, a differenza di quanto mi era stato detto, quello su blogsource non è ancora sparito e quindi potete commentare su ratta.blogsource.com . Se volete a tutti i costi commentare su LiveDigital ma non volete iscrivervi mandatemi il commento per mail a carlo punto bottiglieri chiocciola gmail punto com e io provvederò a metterlo sul blog.
Che tristezza, vivo di copy paste proprio sul mio blog e ho un’interfaccia per i commenti che va contro i requisiti di qualunque utente.