martedì 14 settembre 2010

Il fisico e il meteorologo o del perché i progetti IT falliscono

I progetti IT normalmente falliscono. Esiste tutta una letteratura di report e di statistiche sui fallimenti (v. rif. in calce). Quella dello Standish Group (1995 e 2009) stima una percentuale attorno al 70% di fallimenti. Più drastico Gartner Group, che in diversi report, stima attorno all'85% i progetti IT che producono ritorni negativi (ROI < 0).

Chiaramente esiste una moltitudine di risposte alla domanda "Perché i progetti IT falliscono", e molti autori forniscono la loro ipotesi. Io credo che il motivo principale sia dovuto a un errato processo di valutazione dei costi e degli effort di un progetto, dovuto a una serie di miti su come si realizza e si sviluppa un progetto informatico.

Comincerò con un aneddoto. Un fisico e un meteorologo passeggiano in un parco. Entrambi guardano il cielo. Il fisico dice al meteorologo: "Non capisco. Noi fisici siamo in grado di predire al secondo la posizione di tutti i satelliti in cielo, e voi meteorologi non siete in grado di dire se domani pioverà o meno". Il meteorologo, per nulla scandalizzato, lo guarda e gli dice: "Hai ragione! Non siamo in grado di dirlo".

Durante il mio ultimo progetto informatico ho assistito alla stessa obiezione. Siamo in una grande fabbrica di aeroplani. "Ma come" dice il Cliente al Consulente Informatico "io sono in grado di dire esattamente quanto ci metterò a costruire e a consegnare al mio cliente un aeroplano, e tu non sei in grado di dirmi quanto ci metterai a costruire il mio sistema informativo e quanto ti costerà". "Hai ragione" dice l'onesto consulente "non sono in grado di dirtelo."

La verità è che di consulenti informatici onesti - o abbastanza coraggiosi da ammettere la dura realtà - ce ne sono pochi. Quando il progetto, com'è normale, esce dal budget previsto in termini di costi e tempi, si assiste al nascere di curiosissime scuse: il costo del vendor è aumentato, i dati storici erano sbagliati, il progettista che aveva fatto le stime aveva bevuto, il nostro miglior sviluppatore è dovuto andare in maternità, il server con i sorgenti si è impastato. Scuse, scuse, giusto?

I progetti IT - tutti quelli di una certa complessità e novità - sono caratterizzati dalla legge: prima volta-primo uso. E' quasi impossibile calcolare con esattezza il costo di qualcosa che non è mai stato tentato. È così unico, così sfaccettato, e ha così tanti fronti che il numero e la dinamica delle sue variabili crea un problema insormontabile a chiunque stia cercando di valutare i suoi costi a preventivo.

Il numero di variabili e la novità sono i tratti caratterizzanti di un progetto IT. La tecnologia cambia. Gli strumenti cambiano. Le soluzioni sono diverse e nuove. Gli sviluppatori bravi usano tecnologie obsolete, quelli che usano tecnologie nuove, sono impreparati e non hanno abbastanza esperienza. Inoltre, nessuno conosce "a preventivo" che cosa sarà effettivamente sviluppato.

Paradossalmente, il modello di project-estimation e di creazione dell'offerta ancor oggi più usato in ambito consulenziale è quello "Waterfall", costituito da sei step seriali:

1. Descrizione (sommaria e vaga) dei requisiti del progetto
2. Valutazione e stima degli effort e delle risorse necessarie
3. OFFERTA MONETARIA AL CLIENTE (spesso "chiavi in mano", tutto incluso e senza possibili variazioni)
4. Analisi funzionale (dove si comincia a comprendere che cosa era sbagliato al punto 2)
5. Analisi architetturale (dove i dubbi della fase 4 diventano certezze)
6. Sviluppo & Test (dove il disastro assume proporzioni irreparabili)

Da tempo esistono risposte e metodologie per mitigare gli effetti di questa errata impostazione. Queste sono figlie del movimento Agile e del concetto di Ciclo di Vita dello Sviluppo del Software (Application Life-Cycle Management).

Queste metodologie, abbastanza sorprendentemente, sono tuttora ignorate dalla gran parte del mondo consulenziale IT e delle aziende che hanno grandi e complesse realtà IT al loro interno. Questo per un motivo fondamentale, e cioè che la "stima a preventivo" è la testata d'angolo cui poggia tutto il business, sia per chi vende che per chi acquista soluzioni IT. Il problema, come si è visto, è che la "stima a preventivo" è quasi sempre totalmente errata, quando non addirittura una menzogna.

Se per un momento facciamo l'ipotesi di poter fare a meno della "stima a preventivo", possiamo immaginare un processo di Project Costing & Estimation efficace e, soprattutto, vantaggioso per tutti gli attori (win-win). Questo processo è un processo iterativo, che si svolge all'interno di uno "sprint" di tempo - solitamente pari a un mese. In un mese - ogni mese - si fanno:

1. Analisi dei requisiti (e backlog)
2. Analisi tecnica
3. Sviluppo
4. Test e stime (di completamento, di budget, di costo totale ecc.)

e poi si ricomincia. Questa iteratività è alla base di qualunque processo di cambiamento in azienda, e risponde precisamente alla necessità di conoscere qualcosa che, all'inizio, è inconoscibile. E' un processo di raggiungimento della verità.

Sono convinto che se le imprese che lavorano nel settore IT non cominceranno a predisporre processi inerentemente iterativi, i fallimenti non potranno che continuare a essere all'ordine del giorno.

Le tecniche di sviluppo agili, l'intero Agile Movement, il mondo Open-Source insegnano tutti che il modello iterativo - che parte inoltre dal basso, e cioè dal codice sorgente - è un modello che assicura risultati grandemente migliori rispetto al Waterfall classico. Le grandi realtà di sviluppo software globale (Google, Microsoft) da tempo seguono questo approccio.

Riferimenti:
STANDISH GROUP: http://www.projectsmart.co.uk/docs/chaos-report.pdf
http://www.standishgroup.com/newsroom/chaos_2009.php
http://www.gartner.com/5_about/news/exec_reports.jsp
http://www.itarchitect.co.uk/articles/display.asp?id=203
http://www.projectsmart.co.uk/project-cost-management.html
http://www.it-cortex.com/Stat_Failure_Cause.htm (altri report di fallimenti)
http://www.infoq.com/presentations/Agile-Management-Google-Jeff-Sutherland

venerdì 2 luglio 2010

Differenza tra 'object' e 'class' in Scala

Una delle cose che trovo più intelligenti nel linguaggio Scala, è la netta distinzione tra Tipo Object e Tipo Class. Il primo è un tipo che può avere soltanto una istanza. Il secondo è un tipo che può avere molteplici istanze.

Il primo ha sintassi 'object', il secondo 'class'.

Chiaramente il primo, essendo sostanzialmente una classe "statica", non va istanziato, i suoi metodi possono essere chiamati direttamente. Il secondo va istanziato.

Ecco un esempio:

class Molteplice(nr : Int)
{
def id = nr;

def parla()
{
println("Buongiorno, sono il Molteplice nr = " + id);
}
}

object Singleton
{
def parla()
{
println("Buongiorno, sono un singleton.");
}
}

object Main
{
def main(args : Array[String]) : Unit =
{
Singleton.parla();
val range = 0.until(10);

for (i <- range)
{
val m = new Molteplice(i);
m.parla();
}

}
}



Questo ci permette di capire subito le intenzioni del programmatore, che deve creare classi solamente dove questo ha realmente senso.

Nella mia esperienza di programmatore OO (in Java, C++ e C#), direi che su 100 tipi in un programma normale, ad esempio di integrazione aziendale, 70 sono singleton (cioè in Scala 'object'), ovvero collezioni statiche di metodi e proprietà, e solo 30 classi vere e proprie (in Scala, 'class').

venerdì 10 luglio 2009

Ereditarietà multipla in Scala

Il problema dell'ereditarietà multipla nei linguaggi fortemente tipizzati (C++) è, molto banalmente, che se un oggetto eredita da due differenti classi che implementano lo stesso metodo, il compilatore non sa quale implementazione di quel metodo associargli (bind).

In Java e in C# perciò si è deciso di abolire l'ereditarietà multipla, e di inserire il concetto di interfaccia. Il problema delle interfacce, però, è che attraverso di loro viene descritto il comportamento di un oggetto (quali metodi sicuramente quel metodo avrà), ma non viene implementato, perché l'implementazione si demanda alla classe di quell'oggetto.

Questa soluzione non è ottimale, perché se nella mia tassonomia di classi ho delle forti "somiglianze" tra una classe e l'altra, la scrittura dell'implementazione dell'interfaccia comune rischia di doversi ripetere da una classe all'altra. Insomma, rischiamo ripetizioni e riscritture.

In Ruby esiste il concetto di mixin, per cui posso "prendere a prestito" del codice che esiste "altrove" e inserirlo nella mia classe, senza doverlo riscrivere. Il problema è che Ruby è un linguaggio di scripting, e non è tipizzato.

Scala è invece un linguaggio tipizzato che permette - in un certo senso! - l'ereditarietà multipla, e lo fa attraverso i "trait".

Ecco un esempio:


abstract class Animale
{
def nome:String;
def classe:String;
def comeMiChiamo:String = ("Sono il " + this.nome + " e sono un " + this.classe);
}

trait Mammifero extends Animale
{
override def classe:String = "Mammifero";
}

trait Rettile extends Animale
{
override def classe:String = "Rettile";
}

class Delfino(aName: String) extends Animale with Mammifero
{
def nome:String = "delfino "+ aName;
}

class Serpente(aName: String) extends Animale with Rettile
{
def nome:String = "serpente "+aName;
}

object Main
{
def main(args: Array[String])
{
parla(new Delfino("Pippo"));
parla(new Serpente("Pluto"));
}

def parla(animale: Animale)
{
println(animale.comeMiChiamo);
}
}


In questo esempio, la classe Animale è una classe astratta, che definisce il metodo "comeMiChiamo" (ok, non mi veniva niente di meglio!). Questo metodo è polimorfico rispetto alla "specie" dell'animale. Definisco perciò due nuove classi, che incidentalmente sono anche "classi di animali": i rettili e i mammiferi.

L'output del programma in console è:


Sono il delfino Pippo e sono un Mammifero
Sono il serpente Pluto e sono un Rettile


Quello che voglio dimostrare, è che posso scrivere in Scala una classe che eredita sia da Animale (il metodo "come mi chiamo"), sia da Rettile oppure Mammifero. Per farlo, "Rettile" e "Mammifero" sono due trait, di fatto la trasposizione dei Mixin per Scala. Se vogliamo, sono delle interfacce, solo che, a differenza delle interfacce in Java, il metodo che espongono è anche già implementato - evitandoci riscritture e copia/incolla nel codice (che è sempre male).

PS: La questione della duplicazione dei metodi alla base del problema dell'ereditarietà multipla, non è risolta, in Scala, è solo "evitata", dal momento che il compilatore, quando si tratta di "trait", fa un bel copia e incolla e non si preoccupa di controllare la coerenza - provate infatti ad esempio a far ereditare a Delfino sia il trait Mammifero che il trait Rettile:


class Delfino(aName: String) extends Animale with Mammifero with Rettile

giovedì 30 aprile 2009

Ajax su Https

Scenario: avete un'applicazione Ajax che funziona benissimo. Switchate il protocollo da Http a Https (SSL) e non funziona più.

La prima cosa che vi viene da pensare (come è venuta a me) è che Ajax - o meglio, l'oggetto XmlHttpRequest - non funziona su Https.

Ricerche e un po' di approfondimenti hanno risolto la situazione.

Intanto confermo che XmlHttpRequest (su qualsiasi browser, in particolare Firefox 3 e Internet Explorer 8) funziona su Https.

Il problema è che la pagina chiamante e lo script Javascript (e tutte le altre eventuali sorgenti che formano la pagina, ad esempio le immagini) devono assolutamente provenire da fonti sicure - cioè da fonti sotto Https.

Nel mio caso, infatti, le pagine venivano servite da una directory virtuale sotto Https, mentre gli script in Javascript erano serviti via Http normale. Se questo è il caso, XmlHttpRequest restituisce un errore di Security.

martedì 14 aprile 2009

Xorg.conf per Intel X3100

Un bug introdotto nella release 7.4 di Xorg (Linux) comporta un problema di visualizzazione nelle schede video Intel X3100 - ad esempio montate sui notebook Lenovo T60.

Questo bug fa sì che fallisca il riconoscimento dei driver corretti in automatico da parte di Xorg, cosicché occorre mettere mano direttamente alla configurazione nel file


/etc/X11/xorg.conf


Dopo una serie di tentativi e di studi, sono approdato alla configurazione ottimale, che riporto qua sotto e che si deve immettere andando a editare il file di cui sopra, ad esempio con:


sudo gedit /etc/X11/xorg.conf


Sostituendo interamente il suo contenuto con questo:


# xorg.conf (X.Org X Window System server configuration file)
#
# This file was generated by dexconf, the Debian X Configuration tool, using
# values from the debconf database.
#
# Edit this file with caution, and see the xorg.conf manual page.
# (Type "man xorg.conf" at the shell prompt.)
#
# This file is automatically updated on xserver-xorg package upgrades *only*
# if it has not been modified since the last upgrade of the xserver-xorg
# package.
#
# Note that some configuration settings that could be done previously
# in this file, now are automatically configured by the server and settings
# here are ignored.
#
# If you have edited this file but would like it to be automatically updated
# again, run the following command:
# sudo dpkg-reconfigure -phigh xserver-xorg

Section "Device"
Identifier "Configured Video Device"
Driver "intel"
Option "XaaNoPixmapCache"
Option "XAANoOffscreenPixmaps" "1"
Option "DRI" "true"
Option "AccelMethod" "XAA"
VideoRam 440320
Option "XvMCSurfaces" "6"
Option "May_Need_ForceBIOS" "1
EndSection

Section "Monitor"
Identifier "Configured Monitor"
EndSection

Section "Screen"
Identifier "Default Screen"
Monitor "Configured Monitor"
Device "Configured Video Device"
EndSection

PS:
Oggi a colazione dicevo a Lucilla: "Vedi, figliola, non è vero che Linux è gratis! Il suo utilizzo impone che se uno trova la soluzione a un problema deve pubblicarla quanto prima su Internet. Fondamentalmente è questo il meccanismo che ha permesso all'Open Source di essere ciò che è oggi."

mercoledì 25 marzo 2009

Switch in Python

Com'è noto Python non supporta la sintassi "switch".

Invece che scrivere interminabili e poco leggibili catene di if/elif/else è possibile risolvere elegantemente la cosa con un dizionario.

Ecco un esempio:

        switch = {
'vigenere': Key(parameters),
'des': DESWrapper(password),
'3des': TripleDESWrapper(password)
}

if crypto_type in switch:
self.crypto = switch[crypto_type]
else:
pass
In questo esempio la funzione imposta l'oggetto self.crypto basandosi su una stringa di input crypto_type, che può assumere i seguenti valori:

  • vigenere
  • des
  • 3des

(come potete immaginare è un programma di crittografia). Il dizionario inizializza l'oggetto che, a seconda della stringa in input, può diventare di tipo "Key", "DesWrapper" o "TripleDesWrapper", passando al costruttore gli argomenti corretti.

Se la stringa crypto_type è trovata all'interno del dizionario switch l'oggetto viene inizializzato, altrimenti il programma passa oltre.

mercoledì 21 gennaio 2009

Nove regole di bellezza per il codice sorgente

Ho sempre amato un aspetto, nella programmazione dei computer, e cioè che le cose possono essere fatte in moltissimi modi diversi, e non c'è sostanzialmente un modo giusto e uno sbagliato di scrivere un programma per computer.

Tuttavia, esiste certamente un modo migliore ed uno peggiore per scrivere software. Ecco alcune norme basate sull'esperienza che vorrei condividere.

(In ciò che segue intendo per "codice" il "codice sorgente" di un programma per computer)

1. Il codice dovrebbe sempre tendere ad essere "bello"

2. Quasi mai la "bellezza" coincide con la "sinteticità", benché certamente un codice ridondante sia un codice brutto.

3. La "bellezza" di un codice è proporzionale alla sua "leggibilità"

4. Il codice "esplicito" è più bello di quello "implicito"

5. Le strutture "piatte" sono più belle delle strutture "innestate"

6. "Sparso" è più bello di "denso"

7. Un codice utile "in generale" (generico) è da preferire ad uno utile "in particolare" (ad hoc). La genericità è bella.

8. Non esiste un problema di programmazione che non sia risolubile attraverso la scomposizione in sotto-problemi. Perciò un codice bello è un codice "composto".

9. L'espressività di un linguaggio di programmazione si misura in quanti modi differenti è possibile implementare lo stesso algoritmo. Un linguaggio espressivo, solitamente è bello.