Archive for January, 2007

Appunti su Domain-Driven Design, di Eric Evans

Tuesday, 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

Thursday, 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.

Thursday, 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

Monday, 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

Sunday, 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.