Appunti su Domain-Driven Design, di Eric Evans

January 30th, 2007

Summary: these are notes I write while reading the book by Eric Evans.

Pungolato da Gabriele, ho iniziato a leggere Domain-Driven Design. Questi sono i miei primi appunti.

Begin by designing the model. L’argomento del libro è la progettazione del software, ovvero il design. Ci sono autori (come DHH in Getting Real) che sostengono un metodo di progettazione che parte dall’interfaccia utente, che viene realizzata come una facciata priva di logica, allo scopo di ottenere feedback dall’utente. Questo approccio mi pare più adatto a un tipo di applicazioni dove la logica del dominio sia molto semplice, praticamente implicata da quello che si vede nell’interfaccia utente; molte applicazioni web ricadono in questa categoria. Un possibile rischio è l’effetto iceberg, che fa sì che quanto più raffinato è il mock-up dell’interfaccia utente, tanto più irrilevante sarà il feedback che otteniamo mostrandola agli utenti.

Evans invece suggerisce l’approccio opposto: inizia a progettare la logica del dominio. Ottieni feedback dall’utente tramite discussioni di fronte alla lavagna, con sketch di diagrammi di classi, simulando esempi di uso, e dimostrando un prototipo delle classi di dominio, pilotate da un’interfaccia utente minimale.

Attack domain complexity, not technological complexity. Noi sviluppatori abbiamo una naturale tendenza ad innamorarci della tecnologia; divoriamo libri sull’ultimo framework alla moda per applicazioni web o per interfacciare gli oggetti con i database. Ma spesso quello che fa la differenza fra successo e fallimento di un progetto è quanto accuratamente gestisce la complessità inerente del dominio. Dovendo scegliere fra studiare la spec di EJB 3.0 e un libro sulla teoria degli strumenti finanziari derivati, uno sviluppatore probabilmente preferirà la prima. Occorre frenare questa tendenza, e fare uno sforzo cosciente per imparare quello che serve del dominio applicativo.

Model rhymes with implementation. Non bisogna fermarsi allo stadio di diagrammi UML sulla lavagna, o in documenti Visio/Rose/Che-so-io. Bisogna costruire un’implementazione, che segue passo passo il lavoro di analisi del dominio.

Develop a common language. Il nostro obiettivo è creare un linguaggio ubiquo per parlare del dominio. Un linguaggio che sia derivato dal gergo del dominio, distillando i concetti che servono e rendendoli più precisi. Un linguaggio che sia usato a tutti i livelli: nelle conversazioni fra sviluppatori e con gli esperti di dominio, nei diagrammi UML, e nei nomi delle classi e dei metodi.

Pattern language. A dieci anni di distanza dal libro della Gang of Four, i pattern restano uno dei veicoli migliori per insegnare design.

XP works best when the developers are design buffs. L’assunzione di XP è che tutto il team faccia design in ogni momento. Ciò è necessario perché in XP non c’è nessuno che ti fa piovere il design dall’alto; e se non si fa design, si scade nel code & fix, che è la triste norma nella nostra industria. Allora in XP è importante che tutti imparino il più possibile sul design. Fowler in PEAA parla dell’importanza di creare un Domain Model (uno dei suoi pattern preferiti.) Ma non spiega come! Si limita a dire che in un Domain Model uno può sbizzarrirsi a fare design object-oriented. Il libro di Evans invece parla solo di come realizzare il domain model.

Domain modelling is not just “find the nouns.” I sostantivi corrispondono a classi del dominio; ma sono altrettanto importanti le regole del dominio, e le attività che vengono svolte.

Accelerate development. L’obiettivo è arrivare a un punto in cui powerful new features unfold as corollaries to older features. Il buon design serve a questo, non a soddisfare un astratto desiderio di “fare ingegneria.”

Proposta Tesi di Laurea interna: http chat server in Erlang

January 25th, 2007

Summary: I’m looking for a student to pick up the task of writing an http chat server in Erlang

I linguaggio di programmazione Erlang è una rara bestia — deriva dai linguaggi di programmazione cari agli informatici teorici, come Lisp e Prolog, e li cala in un contesto industriale con un’implementazione di tutto rispetto.

Erlang nasce da una ricerca in Ericsson di un linguaggio di programmazione alternativo al C per sviluppare le centrali telefoniche. Intorno al 1982 Ericsson ha condotto esperimenti con una ventina di diversi linguaggi di programmazione. Gli esperimenti dimostrarono che i linguaggi che fornivano una maggiore produttività in quest’ambito applicativo erano i linguaggi simbolici di alto livello, come Lisp e Prolog. Un fatto curioso emerso da queste ricerche fu che il numero di errori in un programma risultò proporzionale al numero di linee di codice, a parità di funzionalità implementata. Questo implicava la ricerca di un linguaggio che fosse molto più conciso del C.

Il contesto applicativo di Ericsson, ovvero il software che permette a Tizio di telefonare a Caio, richiede però alcune caratteristiche addizionali che questi linguaggi simbolici non avevano: la gestione della concorrenza, la possibilità di contenere l’impatto degli inevitabili errori di programmazione, la scalabilità, la fault tolerance, il soft real-time.

Questa ricerca condusse quindi all’invenzione di un linguaggio ad hoc. Ora, un linguaggio di programmazione vive o muore sulla base della qualità della sua implementazione. E il linguaggio Erlang ha un interprete che permette di realizzare applicazione massicciamente concorrenti (si pensi a quante telefoni comunicano contemporaneamente con una centrale telefonica), con la possibilità di aggiornare il codice a caldo (un po’ come la possibilità di cambiare i dischi in un server senza spegnerlo, solo che qui si tratta di software.) Erlang gestisce la concorrenza con un modello ad attori che è più semplice per il programmatore del meccanismo basato su stato condiviso e semafori. Erlang ha un database transazionale integrato, che può essere distribuito in maniera trasparente su più nodi per fault tolerance e scalabilità. C’è uno studio che dimostra che un web server implementato in Erlang ha una capacità di sopportare picchi di carico di almeno un ordine di grandezza maggiore di Apache.

Tutto ciò porta a concludere che Erlang è il candidato ideale per realizzare applicazioni client-server che necessitino della massima scalabilità e affidabilità: ad esempio i mercati telematici.

L’obiettivo di questa Tesi è di realizzare un progetto pilota di servizio web interattivo push in Erlang. In una parola, realizziamo un server di “chat” tipo lingr o campfire, interamente in Erlang. Questo coinvolgerà l’adattamento di Erlang allo stile di programmazione comet.

Questa Tesi è di livello specialistico, ma possiamo vedere di ritagliarne un pezzo più piccolo per una laurea Triennale. Se sei interessato, mandami un messaggio per prendere appuntamento per parlarne. E’ preferibile avere un po’ di esperienza con la programmazione in stile funzionale.

Aggiornamento: i programmatori pragmatici stanno per fare uscire un libro su Erlang. Questa è una grande notizia!

The Comet is coming; or, push is coming back.

January 25th, 2007

Summary: renewed interest in “push” applications for the web

Il rinascimento in atto di Javascript permette di realizzare applicazioni sempre più sofisticate. Un pezzo difficile da realizzare però è un meccanismo per mandare informazioni sulla pagina web in maniera asincrona: pensate a una chat che deve aggiornarsi su tanti browser ogni volta che qualcuno, da qualche parte, scrive qualcosa.

Il web non supporta nativamente questo tipo di funzionalità. La maniera tradizionale di sopperire è il polling, che consiste nel bombardare ripetutamente di richieste il server per vedere se c’è qualche evento da riferire. Il problema con il polling è che se la frequenza è troppo elevata, il carico sul server cresce anche quando non c’è vera attività. Se la frequenza è troppo bassa, l’applicazione risponde più lentamente agli eventi.

La maniera di evitare questo compromesso fra latenza e frequenza è di fare un “polling lungo,” ovvero tenere in piedi una connessione aperta dal browser al server. Il server, invece di chiudere la connessione, la tiene aperta e trasmette qualche cosa solo quando c’è un evento da riferire. Chiaro che per implementare questo meccanismo occorre un web server configurato in maniera particolare.

Per inciso, questo tipo di meccanismo non è una novità. Da parecchi anni esiste la libreria pushlets. E in una tipica forma di ingiustizia informatica, il concetto si sta popolarizzando ora sotto il nome di comet.

Uno dei problemi di comet è che i web server non sono architettati per il tipo di traffico di queste applicazioni push. Di solito si assume che le connessioni al web server abbiano vita breve; in questo caso abbiamo invece un numero di connessioni che restano aperte pari al numero di client. Un web server di solito usa un algoritmo per cui per ogni connessione viene allocato un thread o un processo; ma questo meccanismo diventa troppo costoso in ambiente comet.

Il web server Jetty usa un’architettura più furba, basata su continuations, per cui quando una connessione comet è inattiva non ha associato nessun thread. I thread vengono pescati da un pool solo quando ci sono dati da trasmettere su una connessione, e subito dopo vengono restituiti al pool. Questo permette di gestire un numero di connessioni molto maggiore del numero di thread.

Su questa base Jetty fornisce non uno ma due meccanismi per realizzare un server push; il primo si basa su un tradizionale ActiveMQ, che è una tecnologia matura e ben integrata con Java tramite il protocollo JMS. Il secondo è un meccanismo più semplice, implementato direttamente in Jetty, basato sul protocollo Bayeux di Cometd. Mentre ActiveMQ fornisce tutte le garanzie di transazionalità di JMS, Cometd è più semplice, non richiedendo un processo addizionale.

Ci sono almeno due librerie Javascript che forniscono l’interfaccia lato client per comet. E uno sviluppo interessante è integrare Rails con comet. E’ possibile, e quelli di lingr l’hanno fatto.

Talk su Test-driven development

January 22nd, 2007

Summary: report of my test-driven development talk at the friday afternoon club of the Milano University C.S. Dept.

Venerdì 19 scorso ho parlato di test-driven development ai vecchi amici della scuola di dottorato in informatica, in via Comelico a Milano. Loro hanno questa simpatica abitudine di fare seminari informali al venerdì pomeriggio, e mi hanno gentilmente invitato. Ho modificato la mia presentazione standard; ho inserito del materiale introduttivo da “A metric leading to agility” di Ron Jeffries, soprattutto prendendo spunto dalla bella intervista che hanno pubblicato su InfoQ.

Di solito come demo uso il kata del bowling di Robert Martin; questa volta, per cambiare, ho presentato il kata del supermercato di Dave Thomas. In Ruby, perché lo trovo più divertente, oltre che conciso.

Devo dire che gli astanti hanno colto subito il punto della cosa, che sarebbe l’idea di fare design durante e non prima. Lo dimostra il fatto che mi hanno fatto numerose obiezioni… La più interessante è stata quella di Andrea, che obietta che lui fa design prima, e riesce ad evitare i problemi di over-design, design eccessivamente anticipatorio, e design semplicemente errato perché, lui dice, è bravo e non fa di questi errori.

Sono il primo a sostenere che la prima cosa che fa la differenza in un progetto è la qualità delle persone; sono perfettamente convinto che un team di waterfallisti capaci possano dare la birra a un gruppo di XPer inesperti (vedi per esempio la copertina di Boehm, Software Engineering Economics.) Detto questo… l’approccio “sono bravo quindi non sbaglio” è rischioso. Tanto per cominciare, come fai a trasmettere questa tua bravura agli altri? Come fai a lavorare in team? Essere l’elemento trainante stufa dopo un po’. Significa che fai la gran parte del lavoro e non riesci a distribuirlo. Mentre tu ti stai impegnando per il tuo ottimo design, gli altri che fanno?

Non mi piace questo approccio direttivo. Io faccio il design, gli altri lo eseguono; mi pare che l’unico risultato possa essere di allevare un team di mediocri. Io vorrei che il design fosse una cosa condivisa da tutti i membri del team. Quando il design piomba dall’alto, chi lo deve eseguire lo subisce. Magari nel tuo team c’è uno veramente in gamba che vorrebbe fare in maniera diversa; ma non può, perché è costretto a fare come dice il design che gli è piombato dall’alto. Il risultato è che presto o tardi questo elemento in gamba se ne va altrove! E’ dura ammettere che quello che vuoi fare tu si possa fare funzionare anche in un’altra maniera. Ma è necessario se si vuole fare un team.

E il tuo cliente, comunque, non potrà toccare con mano la tua bravura fino a quando non avrai finito il tuo design e il team non potrà cominciare a implementare.

Resto convinto che il design evolutivo sia la maniera più efficace di fare design.

(Slide della presentazione)

Il potere degli esempi

January 7th, 2007

Summary: executable acceptance tests are an essential part of any software specification. Even the use of formal specifications in the form of mathematical formulae does not eliminate the need for executable acceptance tests.

Example isn’t another way to teach, it is the only way to teach

— attribuito ad Albert Einstein

Ci sono essenzialmente due maniere di scrivere una specifica per un programma: in linguaggio naturale, oppure usando un linguaggio formale. La tesi di questo articolo è che né l’uno né l’altro stile sono efficaci se non sono accompagnati da esempi. Anzi, è molto più fruttuoso pensare a una specifica come un insieme di esempi, corroborati da una descrizione in linguaggio naturale e/o formale.

Ci sono alcuni problemi che sono molto facili da specificare in linguaggio naturale; ad esempio “sostituisci ogni sequenza di due o più lettere uguali con una sola lettera”. Eppure non siamo sicuri di avere capito che cosa si intende fino a quando non abbiamo visto almeno un paio di esempi:

 "abc" -> "abc"
 "aaaabbcccccd" -> "abcd"

Solo ora possiamo essere sicuri di avere capito che cosa intendeva la specifica. E’ vero che la specifica originale era ambigua; ad esempio non specificava cosa succede per le sequenze di una sola lettera, e neppure diceva quale lettera deve essere sostituita al posto della sequenza; e si potrebbe continuare. Potremmo allora rendere la specifica in linguaggio naturale più precisa, specificando in maggiore dettaglio che cosa deve succedere in tutti i casi. Probabilmente la specifica diventerebbe dieci volte più lunga. Prova! Il risultato sarà senz’altro più preciso; ma sarà anche più chiaro? Probabilmente no; la specifica più diventa lunga e dettagliata e più diventa difficile da capire. In ogni caso saremo costretti a verificare se la nostra comprensione della specifica è corretta andando a vedere gli esempi.

E qui ti racconto un piccolo sporco segreto di noi programmatori: quando riceviamo un tomo di specifica di qualche centinaio di pagine, andiamo a vedere solo gli esempi. Se ci sono dei disegni che mostrano l’interfaccia utente, andiamo a vedere subito i disegni, ignorando il testo che li accompagna. Perché? Perché normalmente le specifiche in linguaggio naturale, nell’intento di essere precise, sono verbose e noiose, ma soprattutto sono poco chiare. Comunicano poco. Niente comunica in maniera efficace come un esempio.

Il nostro spirito scientifico-ingegneristico si ribella allora a questa situazione di pressapochismo. Giustamente cerchiamo una maniera più precisa e meno ambigua di specificare un software che, oltre a tutto, è di norma oggetto di obblighi contrattuali. Come potremmo specificare formalmente, allora, il problema di cui sopra?

Una maniera è scrivere una formula matematica. Però mi risulta difficile immaginare quale, per questo specifico esempio. Provo:

Sia * l’operatore che prende due lettere uguali adiacenti in una stringa e le sostituisce con una delle due;
sia P l’operatore che prende un operatore e lo applica a una stringa fino a quando è possibile. Allora la nostra funzione f è specificata da

   f = P*

Ummmm…. non molto chiaro vero? Provo in un’altra maniera. Definiamo il significato di “comprimere le sequenze” con una funzione definita ricorsivamente:

  
   f [] = []     -- trasformo la stringa vuota in stringa vuota
   f [a] = [a]   -- e il singleton in singleton
   f ([a,a] + x) = f([a] + x)  
                 -- se la stringa inizia con due caratteri 
                 -- uguali, ne tolgo uno
   f ([a,b] + x) = [a] + f([b] + x)  if a != b
                 -- se la stringa inizia con due caratteri 
                 -- diversi, tengo il primo e continuo

Personalmente mi piace questa definizione. E’ chiara e precisa, e anche ragionevolmente concisa. Ma è un programma. E non è immediatamente evidente che corrisponda alla mia idea di cosa significa “comprimere le sequenze”. L’unica maniera di essere sicuro è testare questa specifica su esempi! E si noti che sto proponendo questa formulazione come la specifica del problema. Perché è la specifica formale più chiara che sono riuscito a scrivere. Quindi non posso verificare che questa definizione corrisponda a una qualche specifica, perché è questa stessa la specifica. Potrei verificare se questa definizione ricorsiva corrisponde a un’altra definizione matematica, ma allora avrei sempre il problema di capire se quest’altra ipotetica specifica è a sua volta corretta. E potrei farlo solo testandola su esempi!

Ora, Dijkstra sicuramente mi direbbe che

Program testing can be used to show the presence of bugs, but never to show their absence!

E.W. Dijkstra

Però Dijkstra, per quanto ho letto o sentito dalle sue labbra, sorvolava sul problema di come ottenere una specifica corretta. Lui dava per scontato che sia sempre possibile scrivere una specifica formale che sia sufficientemente chiara da essere evidentemente corretta. Io invece sostengo che questa cosa non è facile, e probabilmente non è possibile in pratica nella maggior parte dei casi. A meno di lavorare in domini molto particolari che beneficiano di migliaia di anni di raffinamento come, ad esempio, i programmi che fanno calcoli matematici. Sicuramente nel software di tipo gestionale, la cosa più difficile è catturare specifiche corrette. Ma soprattutto, una specifica formale difficilmente può essere capita dal cliente; non così gli esempi, che possono essere verificati, anzi, dettati dal cliente stesso.

E qui entra il discorso dei test di accettazione automatici. Nei metodi agili vengono spesso detti customer tests, perché vengono scritti con o addirittura dal cliente. Sono esempi di come deve comportarsi il software, espressi nel linguaggio di business del cliente e non nel linguaggio tecnico dello sviluppatore. Lo scopo dei customer test non è tanto di verificare che il software sia stato realizzato correttamente; anche questo, senz’altro, ma il punto fondamentale è verificare che il software che realizziamo sia il software che veramente il cliente vuole.

Quando si inizia l’intervista per raccogliere i requisiti per un nuovo progetto, il cliente di solito ce ne dà fin troppi. Per esempio, il problema di calcolare la tariffa di un noleggio auto è senz’altro complicato: dipende dal modello dell’auto, dal chilometraggio, dall’età del guidatore, dall’ora di prelevamento e di consegna, distinguendo fra tariffa notturna e diurna; e da chissà quanti altri dettagli. L’unica maniera di essere sicuri di avere afferrato correttamente le specifiche del nostro cliente è di sottoporgli degli esempi concreti. Dunque, se ho capito correttamente, un guidatore di 30 anni prende una Sedici lunedì mattina alle 10:00 e la restituisce martedì alle 18:00 con il serbatio pieno. Allora si applica la tariffa standard per 1 giorno + 8 ore? E il cliente stesso a questo punto ci dirà se abbiamo capito. Il cliente debugga la comprensione della specifica da parte dello sviluppatore.

Un team agile catturerà questi esempi in test che siano sia eseguibili che comprensibili dal cliente. Spesso si usano strumenti come Fitnesse per catturare le specifiche in tabelle HTML come questa:

eg.Division
numerator denominator quotient?
10 2 5
12.6 3 4.2
100 4 24

Il cliente può così non solo verificare che il team ha capito di cosa si sta parlando; può chiarificare a sè stesso quali siano i requisiti; e può verificare egli stesso, eseguendo il test, che il software consegnato si comporti correttamente sugli esempi:

eg.Division
numerator denominator quotient?
10 2 5
12.6 3 4.2
100 4 24 expected


25.0 actual

Dunque gli esempi eseguibili hanno un pubblico molteplice:

  1. il cliente che li può leggere e anche scrivere
  2. gli sviluppatori, che li prendono come specifica;
  3. il software da sviluppare, che da essi viene testato;
  4. e da ultimo, purtroppo, vanno menzionati anche il giudice, il CTU e i CTP che, nel malaugurato caso di una lite, avranno una molto migliore probabilità di capire che cosa si suppone che il sistema faccia.

Anche se la presenza di una specifica ben scritta (cioè basata su esempi) rende meno probabile l’eventualità di una lite. Nel nostro mestiere il più delle liti sorge su questioni del tipo “intendevo che mi sviluppaste questo”, “no, non era questo che avevi detto!”. Sviluppare la specifica di concerto per mezzo di esempi serve anche a scongiurare esiti di questo tipo.

La mia conclusione è che la maniera tradizionale di presentare le specifiche come “testo eventualmente accompagnato da esempi” oppure “formule matematiche eventualmente corredate da esempi” debba essere scaravoltata.

Gli esempi sono la parte più importante della specifica.

Corredarli con testo esplicativo oppure un modello formale può essere utile; ma dovendo buttare via qualcosa, gli esempi sono l’ultima cosa a cui rinunciare.

Microsoft brevetta RSS

December 23rd, 2006

Summary: it seems Micro$oft is trying to patent RSS

Per la serie “i brevetti servono a stimolare l’innovazione”, abbiamo la società dell’uomo più ricco del mondo che brevetta una cosa che è stata inventata molti anni prima da persone che non lavorano per Micro$oft. Che vergogna. E comunque non c’era bisogno di un altro argomento per dimostrare che tutti i brevetti sul software sono ingiusti.

Do they think it’s good that Microsoft comes into a market that was doing pretty well without them, and before they ship a single product, are already putting up barriers to keep others out? That’s good?? Really. Why?
Dave Winer

Three interesting Java libraries

December 22nd, 2006

Sommario: tre interessanti librerie Java

The standard Java date/time classes suck. There’s not much arguing about that. You either use the good, old, reasonable java.io.Date constructors that take year, month and date, but are deprecated thanks to the political correctness police; or you use the brain-damaged GregorianCalendar that wants, hear this, 0-offset months (that is, you pass it 2 and it means March). I’ve seen novices and not-so-novice programmers waste heaps of time trying to do simple date manipulations that should be dead easy. I certainly have.

So it’s good luck that some good people wrote the Joda library: it’s a simple and sane complete replacement for the Java date and time classes.

  *       *
      *

David Eric Evans is the author of Domain Driven Design, that is sitting in my pile of Books I Will Read Next since a long time. This book teaches how to concentrate on the “domain” part of software, which is in a sense a return to object-oriented basics. Recently David Eric released a free summary of his book. In the interview that goes with it I found out about the Time and Money library, that implements in Java a fragment of domain model that is common to many business applications. This is worth looking into, in a “let’s read software not just write it” way.

  *       *
      *

I strongly dislike Java Server Pages. It’s not the idea of mixing Java and Html that I don’t like; in fact, I’m perfectly comfortable with Rails’ ERuby templates. The thing I don’t like is that it’s very difficult to test them; you essentially need to run them in a servlet container, and that makes it nearly impossible to unit test them.

For this reason a while ago I used Velocity in a web application project. I’m not too keen on Velocity either, since it’s way more complicated than necessary to set up. So I’m happy that there are alternatives: the now apparently dead OpenSails project has the Viento template engine. And Terence Parr, the author of the ANTLR parser generator, wrote StringTemplate.

  *       *
      *

So in the end that makes it four libraries. I lied :)

Merry Christmas — Un Natale di Pace a tutti!

Ma quale ingegneria?!

December 19th, 2006

Summary: they often say that software engineering is about transitioning from craftsmanship to craft + science. The flaw in this reasoning is that practice in software development is not yet at the craftsmanship stage. (By a similar reasoning we see that even computing science is not really science, but is at a pre-science stage.)

Employers … complain that they do not know what a graduate of either a CS program or a traditional engineering program can be expected to know about software development.

— David Parnas

Si sente spesso dire che l’idea di software engineering consiste nel passare da uno stadio artigianale, caratterizzato da modi di lavorare provati dall’esperienza, a uno stadio scientifico, in cui la scienza ci permette di andare oltre gli schemi tramandati dalla tradizione per scoprirne di nuovi.

Sono perfettamente d’accordo con questa immagine di come la scienza, combinata a un sapere empirico artigianale, conduce all’ingegneria. Nel caso dello sviluppo software, però, c’è un piccolo problema. Noi non siamo ancora a uno stadio artigianale. Siamo a uno stadio pre-artigianale.

E’ facile dimostrare questa mia asserzione. Si prendano due praticanti qualsiasi dello sviluppo software (e intendo persone che siano realmente in grado, personalmente, di realizzare software; non astratti architetti che promulgano documenti che vengono puntalmente ignorati da chi fa il lavoro veramente.) Si prendano, dicevo, due praticanti a caso. Non saranno d’accordo su praticamente nulla. In questo mestiere non ci sono metodi universalmente riconosciuti come validi. Dalla maniera di scrivere i commenti fino alla scelta degli strumenti, i nostri due ipotetici praticanti saranno in totale dissenso su quasi tutto.

E questo resta vero anche se scegliamo due praticanti a caso all’interno della stessa tribù; ad esempio due sviluppatori Java o due sviluppatori C#. Saranno d’accordo sulla scelta del linguaggio di programmazione, ma cominceranno a discutere sulla maniera di accedere al database, sulla scelta dei framework e, indubbiamente, sulla maniera di scrivere i commenti.

Che cosa caratterizza l’artigianato? La cosa che maggiormente lo caratterizza è l’apprendistato: il giovane apprendista acquisisce il suo know-how direttamente da un artigiano di provata capacità. In questo modo si formano scuole che hanno un loro modo di fare le cose. Ci saranno senz’altro grosse differenze fra il modo di lavorare delle scuole, così come l’architettura tradizionale ha un suo diverso carattere in tutte le parti del mondo; ma se prendiamo due artigiani a caso nella stessa scuola avranno un insieme di modi di lavorare condivisi.

Nel mondo dello sviluppo software invece… Sappiamo benissimo che non è così. Ognuno impara a fare le cose essenzialmente da solo. All’università si lavora da soli, o magari in gruppo con altri studenti che però sono anche loro inesperti. Al lavoro ci si aspetta che ognuno lavori in maniera indipendente. Non capita mai che a un progammatore junior si affianchi uno senior che lo segua e spenda del tempo per trasmettergli il know-how. Lo so bene, per le reazioni scioccate che osservo quando propongo di fare pair programming: il capo progetto obietta “ma così spendo il doppio per fare le stesse cose,” senza capire che così sia lo junior che il senior progrediscono rapidamente e riescono a produrre un valore che è ben maggiore della somma di quello che potrebbero realizzare lavorando sempre da soli.

Immaginate una cosa simile in una società preindustriale, se ogni muratore imparasse da solo, senza guida di un esperto, a costruire i muri. Salterebbe fuori che 99% dei muri sarebbero storti e sbilenchi, e fragili. Solo quell’1% di persone baciate dalla fortuna di un talento naturale riuscirebbero a costruire un bel muro dritto e robusto.

Noti qualche somiglianza con il mondo dello sviluppo software oggi? Hai provato a osservare l’orrendo casino che passa per codice scritto dai programmatori professionisti?

Se hai seguito il mio ragionamento, dovresti trovare facile capire, a questo punto, il grande successo di Extreme Programming e degli altri metodi agili. Sono un tentativo di uscire dalla fase pre-artigianale per raccogliere un corpus di metodi provati dall’esperienza. Kent Beck ha detto che XP non è altro che una codifica organica di cose che i migliori programmatori hanno sempre fatto. Sono molto contento di quello che vedo nella scena XP, perché i practitioner hanno una comprensione condivisa di una serie di cose che sono state provate efficaci dall’esperienza. Non solo: c’è una condivisione di valori, che permettono di lavorare insieme, al di là delle preferenze personali in fatto di linguaggi di programmazione.

Posso concludere il mio ragionamento su una nota polemica? Certo che sì, questo è il mio blog. Estendiamo il mio esperimento concettuale di prima al mondo della computing science o informatica. Prendiamo due computer scientist a caso: non saranno d’accordo su praticamente nulla. Con ogni probabilità le rispettive tribù parleranno linguaggi così diversi che non saranno nemmeno in grado di spiegarsi l’un l’altro in cosa consiste il loro lavoro. Il mondo della ricerca informatica è finemente suddiviso in miriadi di sottotribù che si occupano magari delle stesse cose, ma usano metodi e linguaggi completamente diversi. Gli articoli pubblicati sono mediamente illeggibili dai non-membri della minuscola tribù dell’autore. Un esempio su tutti: lo studio della concorrenza. Dalle cosiddette “teorie dei processi” di Milner/Hoare alla teoria dei linguaggi di programmazione alla teoria delle basi di dati, non c’è verso di avere non dico una teoria, ma nemmeno un linguaggio unificato su problemi che alla fine sono comuni. Con il risultato che fra la ricerca e la pratica in informatica c’è un baratro di incomunicabilità, che tende ad allargarsi.

[Grazie a Federico per la discussione che ha fatto emergere questi pensieri]

Saggiare le rotaie

December 15th, 2006

Summary: nice screencast about doing proper testing in Ruby on Rails

Ho l’impressione che ci sia poca documentazione in giro su come fare sviluppo test-driven in Rails. Il che è un peccato perché il framework offre ottimo supporto per il testing. Non è male questo screencast, che purtroppo non è gratis ma costa un modesto $9. Permette di vedere in azione uno che ha molta pratica con il framework. Fa una rapida dimostrazione delle tecniche basilari di test, e dimostra parecchi plugin addizionali, sia per il testing che per altro. Il codice dell’applicazione di esempio è fornito.

Va detto che il tipo non fa TDD “by the book”, ma spesso scrive codice in anticipo rispetto ai test. Mi piacerebbe vedere un documento scritto bene (non screen/podcast) su TDD e Rails. Se lo trovi, apprezzerei un link!

Classic paper: On the Criteria To Be Used in Decomposing Systems into Modules

December 5th, 2006

Summary: my comments on a classic paper by David Parnas

I would advise students to pay more attention to the fundamental ideas rather than the latest technology. The technology will be out-of-date before they graduate. Fundamental ideas never get out of date. However, what worries me about what I just said is that some people would think of Turing machines and Goedel’s theorem as fundamentals. I think those things are fundamental but they are also nearly irrelevant. I think there are fundamental design principles, for example structured programming principles, the good ideas in “Object Oriented” programming, etc.

David Lorge Parnas ACM Fellow Profile

Uno dei pochi, pochissimi principi universalmente accettati nell’ingegneria del software è che l’unica maniera di scrivere un programma di grandi dimensioni è scomporlo in moduli. Quello che non è completamente chiaro è come scomporre il sistema in moduli; qual è un principio razionale per decidere quale funzionalità deve andare in quale modulo? Questo problema sembra banale, e non degno di attenzione accademica. Eppure è fondamentale per la realizzazione di un buon design.

When you watch programming from the outside, everyone seems to love the hard problems and its widely celebrated as the interesting accomplishments.

But there are so many simple problems that haven’t been solved. Or haven’t been solved well. I’d rather solve them.

David Heinemeier Hansson, Not so clever; or why I don’t like hard problems

Nel suo classico On the Criteria To Be Used in Decomposing Systems into Modules, Parnas sostiene che ogni modulo esiste per nascondere un segreto. Il termine “information hiding” è opera sua. Un’altra maniera di vedere questo concetto è di pensare a tutte le decisioni di progetto che bisogna prendere per realizzare un sistema: quale algoritmo, quale database, quale … e nascondere ciascuna di queste decisioni in un modulo. E come possiamo valutare la bontà di una scomposizione in moduli? Si ipotizza di cambiare una decisione, e si vede se ci tocca modificare un solo modulo (bene) oppure più di un modulo (male).

The … decomposition was made using “information hiding” as a criterion. The modules no longer correspond to steps in the processing. The line storage module, for example, is used in almost every action by the system. … Every module … is characterized by its knowledge of a design decision which it hides from all others. Its interface or definition was chosen to reveal as little as possible about its inner workings.

We have tried to demonstrate by these examples that it is almost always incorrect to begin the decomposition of a system into modules on the basis of a flowchart. We propose instead that one begins with a list of difficult design decisions or design decisions which are likely to change. Each module is then designed to hide such a decision from the others. Since, in most cases, design decisions transcend time of execution, modules will not correspond to steps in the processing.

E’ difficile non essere d’accordo con Parnas: ogni modulo racchiude una decisione. Realizzare questa idea però può non essere facile in pratica. Credo che ci vogliano esperienza, fantasia e impegno per riuscire a “contenere” le decisioni in un modulo, in maniera che sia possibile poi modificare queste decisioni senza alcuna ripercussione sul resto del sistema. Io vedo questo come un’istanza del problema di rimuovere la duplicazione.

Ad esempio, se nel mio programma Java devo leggere dei file, mi ritroverò a scrivere questa noiosa pappetta in più punti del mio programma:

  BufferedReader in = new BufferedReader(new FileReader("infilename"));
  String str;
  while ((str = in.readLine()) != null) {
      process(str);
  }
  in.close();

(grazie a javaalmanac.com per lo snippet.) A prima vista niente di strano; le operazioni di I/O sono incapsulate nelle classi di java.io in perfetto amore object-oriented. Eppure questo frammento racchiude una serie di decisioni: e se io non volessi più usare il BufferedReader? E se volessi specificare un encoding? E se volessi testare senza toccare il filesystem? Sarebbe meglio incapsularlo in un modulino:

  MyReader in = new MyReader("infilename");
  while (in.hasNextLine()) {
    process(in.nextLine());
  }

o anche

  MyReader in = new MyReader("infilename");
  in.eachLine(process);

Questo per dire che non sempre è facile riconoscere la duplicazione ed eliminarla.

The DRY (Don’t Repeat Yourself) Principle states:

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

Don’t Repeat Yourself

che può essere espresso anche come

Each and every declaration of behavior should appear OnceAndOnlyOnce.

Once And Only Once