Javascript è un linguaggio sempre più usato per scrivere applicazioni complesse.
Tuttavia la sua enorme versatilità fa sì che spesso il codice diventi un incubo da manutenere.
Il costo sopportato dalle aziende per la sola manutenzione del software rappresenta l'80% mentre lo sviluppo di nuovo codice solo il 20%. Questo significa che anche se investiamo il doppio del tempo per scrivere codice più facile da manutenere, siamo destinati a trarne comunque enormi benefici più avanti.
Scrivere codice manutenibile vuol dire scrivere codice di qualità. Codice che sia in primo luogo semplice da comprendere (tramite commenti e indentazione), e semplice da manutenere, ovvero facilmente modificabile, estensibile, e debuggabile (tramite un uso sapiente di oggetti ed errori)
Uso personalmente Javascript in una applicazione con 9 oggetti, per un totale di poco meno di cinquemila righe, con due client separati che utilizzano integralmente la stessa logica, dalle stesse librerie javascript. L'applicazione è spesso soggetta a richieste di modifiche da parte dell'utente; dopo 6 anni di vita, posso consigliare le pratiche migliori da adottare sul campo; e dopo aver visto l'intervento di Nicholas Zakas sulla manutenibilità del codice, ho deciso di pubblicare in italiano le mie considerazioni, unite alle sue, corredandole di esempi di codice.
Nessuna di queste indicazioni vuol essere un dogma; tuttavia prendetele in considerazione sempre prima di operare una scelta diversa.
1. L'ambiente di lavoro. Risparmiare tempo e fatica (se non addirittura mal di testa)
Scrivere javascript può essere facilitato da alcuni strumenti: editor evoluti, quali homesite, director ecc., ed ambienti un po' più sofisticati, come Aptana. Ne cito solo un paio, le scelte sono veramente infinite.
Tutti gli ambienti hanno le loro limitazioni, ma un ambiente come Aptana, che includa il completamento automatico non solo delle classi native ma anche degli oggetti creati da noi, fa risparmiare tanto tempo.
Firefox con firebug installato, e naturalmente anche qualche virtual machine con vari sistemi operativi e browser, così da ben rappresentare almeno il 99% della vostra utenza. Guardate le statistiche del sito per sapere che browser vengono utilizzati. Ma ricordate che alcuni browser mentono spudoratamente.
Nota: anche se il codice è scritto per una applicazione che sicuramente girerà sempre su IE, nulla vieta che un comando venga modificato senza alcun preavviso. Lasciandovi a piedi.
Scrivere codice cross-browser assicura una maggiore aderenza a standard che essendo tali sono probabilmente destinati ad essere più longevi.
Infine, il CVS. In un team di sviluppo è fondamentale gestire gli accessi ai sorgenti, e poter ripercorrere la storia delle versioni di un file. (vedi sotto "non rinunciare alla build")
2. Comprensibilità
Il codice in primo luogo deve essere comprensibile.
Questo significa scegliere delle convenzioni (per i nomi degli oggetti, delle istanze, delle variabili e per l'indentazione) e seguirle! Un documento di specifica aiuta in questo processo.
Una funzione con un nome approssimativo oppure addirittura priva di commento, può costringerci a leggere decine di righe di codice per comprenderne l'uso. E' probabile che qualcuno riscriva la funzione prima di rendersi conto che esiste già! Una funzione che si chiami rimuoviAppuntamentiScadutiEFestivi() è sicuramente migliore di remscad().
Commentate anche gli algoritmi più concisi o difficili, e - sempre e comunque - qualunque hack.
Il commento di ciascuna funzione non deve essere una ripetizione del suo nome; se c'è bisogno di chiarimenti, aggiungeteli; altrimenti concentratevi sul motivo per cui avete creato questa funzione, e sul modo in cui si intende che venga usata. Un esempio dei parametri che può ricevere tornerà molto utile in futuro.
Non usate i nomi "pippo" "pluto" e "paperino" per le vostre variabili: ogni variabile deve avere un nome significativo. Unica eccezione sono gli iteratori, che siete liberi di chiamare i,j,k.
La notazione ungara può aiutare molto nel javascript - che non tipizza le variabili: il prefisso delle variabili ne indica il tipo, es szNome o sNome, iLeft ecc...; una alternativa può essere quella di inizializzare le variabili così che almeno dalla dichiarazione sia chiaro l'intento iniziale:
var dataUltimoAppuntamento='15/10/2009';
Infine, funzioni e variabili minuscole, istanze di oggetti maiuscole, e, se ci sono più parole a comporre il nome, dalla seconda in poi con iniziali maiuscole.
3. Separazione di contenuti, presentazione e codice.
Come il PHP e il Perl, anche il Javascript si presta alla malsana pratica di mischiare contenuti e codice.
3/A Collegare gli eventi nel codice, non nel markup:
No:
<a onclick="inviaRichiesta()">
Si:
document.getElementById('aInviaRichiesta').assignEvent(inviaRichiesta)
<a id="aInviaRichiesta">
3/B: Generare html e testi da costanti o modificare quello esistente.
Spesso, in risposta ad una richiesta asincrona ad un webservice (si, vuol dire Ajax) è frequente generare intero codice html.
Prima di tutto verificate che non sia possibile avere un <div id="appuntamentoOra"> di cui modificare l'innerHTML da codice.
Usate sempre le costanti per le URL e per i testi: un traduttore non rischierà di cancellare un apice.
E le URL cambiano in fretta... meglio non dover fare ricerche.
No:
alert('L\'appuntamento è scaduto')
Si:
Definire costanti per l'html nel codice:
var Costanti = {
AppScaduto : "L'appuntamento è scaduto",
AppCancellato: "L'appuntamento è annullato"
URLNoLogin: "/loginerrato.php"
};
alert(Costanti.AppScaduto);
document.location.href=Costanti.URLNoLogin;
No:
element.innerHTML = '<a xhref="/">....'
Si:
Definire costanti per l'html nel codice:
var CHTML = {
AppLine_1 : "<a id=",
AppLine_2 : ">",
AppLine_3 : "</a>"
};
element.innerHTML = CHTML.AppLine_1 + 1072 + CHTML.AppLine_2 +
"Sergio visita" + CHTML.AppLine_3
3/C Niente javascript nei fogli di stile.
Infine, uno stile può contenere javascript: es.
div.hd {
width:
expression(ref.offsetWidth+"px");
}
Lo so che è comodo. Però è un incubo da debuggare. E unisce la logica di presentazione al contenuto in maniera scomoda.
3/D Evitare il CSS in Javascript:
element.style.color="red"
ma piuttosto
element.class = "appuntamentoScaduto";
laddove nel file .css avrete
.appuntamento {}
.appuntamentoScaduto {}
4. Gli eventi non devono contenere logica di business.
In un gestore eventi, es. onclick, è giusto eseguire solo le informazioni di raccolta informazioni relative all'evento; tutte le operazioni dovranno essere eseguite da una funzione (o meglio un metodo di un oggetto) esterno:
function dataKeyPress(event){
if (event.keyCode == 13){
Agenda.apri();
}
}
5. Crea oggetti per le tue esigenze e racchiudili in un file separato.
Se voglio creare una agenda, meglio creare un oggetto
Agenda
che contenga tutte le proprietà, inclusi gli appuntamenti, ed i metodi, piuttosto che avere una lista di funzioni globali che interagiscono con altrettante variabili e oggetti globali.
Usa i namespace in una applicazione complessa; permetteranno di scrivere codice più chiaro, invece di avere apriAgenda() apriAppuntamento() ecc. avremo
Agenda.apri() Agenda.Appuntamenti.apri('10/10/1971');
6. Non modificare oggetti di Javascript
Se il metodo Array.Join si comporta diversamente da come vorresti, non modificarlo. Piuttosto crea un metodo JoinWithCRLF; questo eviterà confusione e mal di testa agli sviuppatori che potrebbero contribuire al tuo codice.
7. Evita codice difficile da debuggare:
Funzioni annidate
closures
Singletons
8. Sii specifico negli errori
Se un metodo può avere parametri non adeguati, solleva una eccezione!
function apriAgenda(data) {
if (arguments.length < 1) {
throw new Error("apriAgenda: data non presente");
}
//... elabora
}
Sarà molto più facile identificare l'errore, e qualora dovesse finire al cliente, non vedrà il terribile "undefined at line 1, row 1" mentre l'errore è a riga 379, o l'ancora più simpatico "undefined is undefined".
8/B. Non comparare con null
if (isnull param)
in realtà non mi dice altro se non che l'oggetto esiste; Ma se ho bisogno di un oggetto di tipo appuntamento è molto più utile fare una verifica tipo
if (param instanceof appuntamento) {
e se ho bisogno di un numero
if (!isNaN(param)) .... e così via
Questo permetterà anche di fornire errori più utili.
9. Non rinunciare alla build
Javscript non richiede procedure di build.
Ma ci sono strumenti estremamente comodi, e gratuiti, per gestire il processo di build: per esempio si può rimuovere commenti e spazi, rinominare automaticamente variabili, e combinare più file in un unico file.
Conviene infatti separare in tanti file .js ciascun oggetto, ed utilizzare un sistema di versioning / cvs per la gestione. Questo permette a sviluppatori diversi di lavorare contemporaneamente a oggetti diversi; e permette anche di distribuire un unico file .js invece di 9 separati.
10. Consulta la documentazione!
Una comoda lista di molte risorse fondamentali sul javascript
http://blog.mootools.net/2007/6/5/help-i-dont-know-javascript
Mozilla Developer Center
http://developer.mozilla.org/en/JavaScript
MSDN JScript Language Reference
http://msdn.microsoft.com/en-us/library/yek4tbz0.aspx
Questo articolo ha preso spunto dalla conferenza tenuta da Nicholas Zakas: "Mantainable Javascript"
http://video.yahoo.com/watch/568351/2820297
e dalla successiva recensione di Lee Thé
http://adtmag.com/article.aspx?id=21263
Infine, qualche trucchetto può sempre aiutare:
http://tmg.it/content/view/31/35/