ls -lst | head -10

Friday, 06 October 06
Ogni tanto quando programmo e devo sincronizzare due diversi rami di sorgenti dopo aver fatto cambiamenti in uno dei due uso il seguente comando
ls -lst | head -10
per vedere quali file ho cambiato (e quali devo aggiornare sull'altro ramo). ls e' il comando che sui sistemi Unix-like, tra i quali Linux, mostra la lista dei file presenti nella directory corrente. Come quasi tutti i comandi Unix, che sono specializzati nel fare una sola cosa molto bene, ha una varieta' di opzioni e features che c'e' da perdercisi. Una di queste e' l'ordinameto, che si ottiene con l'opzione -s (sort). Nel caso specifico -st (sort time) ha l'effetto di ordinare per tempo di modifica, i file modificati recentemente sono quelli che compaiono prima, seguiti dai piu' vecchi. L'opzione -l specifica la modalita' estesa di visualizzazione dei file. Per finire si passa il tutto in pasto ad head -10 che mostra solo le prime 10 righe dell'output, e abbiamo un sistema rudimentale per rispondere alla domanda "Che file ho cambiato nell'ultima sessione di sviluppo?".

Ecco un esempio eseguito contro i sorgenti di questo blog:
% ls -lst | head -20
total 132
 4 drwxr-sr-x  2 antirez antirez 4096 Oct  7 12:05 noweb/
 4 drwxr-sr-x  2 antirez antirez 4096 Oct  7 12:03 css/
 8 -rw-r--r--  1 antirez antirez 7639 Oct  7 11:47 blog.php
 4 -rw-r--r--  1 antirez antirez 1004 Oct  7 03:02 header.php
 4 drwxr-sr-x  2 antirez antirez 4096 Oct  6 12:53 m/
 4 -rw-r--r--  1 antirez antirez  661 Oct  6 11:53 config.php
 4 -rw-r--r--  1 antirez antirez 1406 Oct  5 18:41 favicon.ico
 4 -rw-r--r--  1 antirez antirez 1039 Oct  5 14:21 index.php
 4 -rw-r--r--  1 antirez antirez 1091 Oct  5 14:18 editblogpost.html.php
 4 -rw-r--r--  1 antirez antirez 1217 Oct  2 19:10 editcomment.html.php
 4 -rw-r--r--  1 antirez antirez  827 Oct  2 14:55 blogpost.php
 4 -rw-r--r--  1 antirez antirez 1448 Oct  2 14:46 blogpost.html.php
 4 -rw-r--r--  1 antirez antirez  417 Oct  2 14:44 outputrss.php
 4 -rw-r--r--  1 antirez antirez   89 Oct  2 13:30 footer.php
 4 -rw-r--r--  1 antirez antirez  706 Sep 30 02:53 limone.png
 4 -rw-r--r--  1 antirez antirez 1839 Sep 30 02:43 addblogcomment.php
12 -rw-r--r--  1 antirez antirez 8614 Sep 28 02:01 utils.php
 4 -rw-r--r--  1 antirez antirez  938 Sep 27 19:49 rss.php
 4 -rw-r--r--  1 antirez antirez 1708 Sep 27 19:49 rsslib.php


A parte noweb che e' una directory dove tengo la struttura del DB, la TODO list e altri file di supporto non necessari al funzionamento della applicazione, le modifiche recenti sono relative al file blog.php (poco fa ho aggiunto una formattazione stile codice sorgente per poter scrivere questo post, che produce proprio i box in font monospace senza alterare la formattazione che vedete qui sopra), una modifica ieri notte all'header (non ricordo cosa ho cambiato alle 3 di notte...), il CSS per la classe .code, e cosi' via.

ls -lst e' un comando utile, ma questo articolo parla del perche' non si dovrebbe usare in un ambiente di sviluppo serio ;) di quello che dovresti mettere su se stai sviluppando una applicazione web per tenere in sincronia il codice sorgente su cui sviluppi con quello in produzione in maniera semi-automatica, e di altre questioni relative allo sviluppo di applicazioni web. Se ti basi su ls -lst, o peggio ancora sulla tua memoria, ci sono buone probabilita' che il tuo sistema di sviluppo sia un incubo :)

Sincronizzazione

Nel caso tipico le applicazioni web vengono sviluppate su computer locali in cui c'e' un ambiente simile a quello di produzione (ad esempio Apache, un server SQL, e cosi' via). Ogni volta che il ramo locale e' pronto per entrare in produzione, o dopo aver fatto semplicemente un fix importante e averlo testato, la nuova versione viene spostata online sul server remoto.

Una complicazione possibile e' data dal fatto che spesso a modifiche consistenti corrispondono modifiche alla struttura del database. In tal caso e' necessario lanciare un paio di ALTER TABLE prima di aggiornare i sorgenti.

Se c'e' una cosa che fa andare veloce lo sviluppo di una applicazione e' la riduzione delle inerzie che non fanno propriamente parte del processo di scrittura del codice, ma che sono delle scomodita' necessarie. Sicuramente la fase di sincronia col server remoto e' una di queste inerzie, non solo, se qualcosa va storto ci sono buone probabilita' di vedersi comparire qualche errore online... per questo e' cosi' importante curare questa fase, ed anche avere un sistema di produzione basato su un sistema operativo potente per il programmatore, come Linux, che consente di risolvere molti di questi problemi tramite un paio di script di shell.

Il modo in cui gestisco questo problema e' creando uno script di shell transfer.sh (o qualcosa del genere) che si occupa tramite scp (di solito si ha un accesso SSH sul server di sviluppo) di trasferire tutti i file della applicazione (ovvero l'intera directory ricorsivamente tramite -r, non vuoi dimenticare l'ultimo file che hai aggiunto immagino ;). Tutti i file... tranne uno:

Se nella tua applicazione hai aggiunto un file di configurazione hai avuto una buona idea. E' il posto in cui puoi impostare ad esempio quante news visualizzare nella home page, quanti tag popolari generare e cosi' via. Se vuoi cambiare un settaggio della tua applicazione basta modificare il file di configurazione. L'importante e' separare tale file da quello che contiene i parametri server centrici, come ad esempio il nome del database e l'account per collegarsi al server SQL. E' utile separare il file relativo al server su cui l'applicazione risiede dal file dalla configurazione globale, chiamarlo qualcosa come localconfig, e includerlo nel file di configurazione: lo script per aggiornare i sorgenti sara' scritto in modo da non aggiornare mai localconfig cosi' i parametri di configurazione di database, domini, e directory specifiche da server a server rimangono inalterati. In questo modo non devi preoccuparti di modificare il file di configurazione dopo il trasferimento. Puoi modificare il numero di tag da generare, trasferire il tutto e vedere la mdoifica online senza per questo dover alterare a mano il file di configurazione dopo il trasferimento.

Quello che hai ottenuto con un simile sistema e' il fatto che quando hai fatto l'ultima modifica al tuo ramo di sviluppo tutto cio' che devi scrivere e': ./transfer.sh - inserire la password del server remoto ed e' fatta, hai l'applicazione in sync.

TIP: Se il tuo link col server e' lento e vuoi evitare che durante la fase di sincronizzazione alcuni file siano aggiornati ed altri no crea uno script (da eseguire da remoto tramite trasfer.sh utilizzando "ssh -c" o qualcosa del genere) che esegue il trasferimento dei sorgenti in altra directory come tar.gz e lo scompatti alla fine.

Strada a doppio senso

Anche con questo sistema, puoi scommetterci, prima o poi accadra' che farai una modifica al volo sul server in conseguenza ad avvenimenti semi-catastrofici ;) Quello che ti serve e' una procedura che funzioni anche al contrario, che dal server sincronizzi i tuoi file in locale. Magari puo' andar bene in questo caso (che e' molto piu' raro) anche uno script is_in_sync.sh che scarica il sorgente dal server ed esegue un diff -r -u /remote /local per mostrarti cosa c'e' di diverso tra le due versioni. Anche in questo caso il file localconfig non deve essere trasferito da una parte all'altra (basta usare l'opzione --exclude del comando tar).

ALTER TABLE

Come si gestisce il DB in tutto questo? Ogni volta che cambia sono dei problemi, bisogna alterare la tabella sul server e puo' accadere di commettere qualche errore. Per ridurre questa evantualita' un sistema abbastanza efficace e' quello di creare nei sorgenti una directory in cui mettere la struttura del database e altre cose del genere (io la chiamo noweb come si vedeva poco fa dal listing prodotto da ls). Se ci sono cose che non volete mostrare al pubblico proteggete con un .htaccess di una linea:
deny from all
e state tranquilli. Ora che avete noweb/db.sql con dentro la struttura del database, ogni volta che alterate la tabella in locale per una modifica della applicazione accodate la riga precisa dell'alter table in questo file. Per ogni ALTER TABLE ci sara' una nuova riga dentro. Prima di trasferire l'applicazione eseguite gli ALTER TABLE uno dopo l'altro sul server remoto... e alla fine trasferite l'applicazione.

Per far si che tutto questo sistema funzioni e' pero' necessario che la versione precedente della applicazione (quella sul server remoto per intenderci) funzioni anche dopo che il database viene aggiornato. Questo non e' un problema se evitate qualunque riferimento implicito ai campi nelle query SQL, dovete utilizzare sempre dei DEFAULT sani e dei riferimenti espliciti.

Ad esempio invece di INSERT INTO mytable VALUES('antirez','Salvatore'); utilizzate INSERT INTO mytable (nick,name)VALUES('antirez','Salvatore');. In questo modo anche se inserite un campo surname la versione precedente del programma continuera' a funzionare.

Per concludere con il problema della sincronia serve ancora un terzo script di shell che aggiorna il database locale con quello remoto. Sul DB in produzione gli utenti producono ogni giorni nuovi dati, probabilmente sara' utile testare il vostro ramo di sviluppo con un database aggioranto anche perche' nelle applicazioni web molto spesso ci sono dei comportamenti realtivi al tempo (ad esempio l'eta' di una news in oknotizie). Se create uno script di shell che in un solo colpo vi scarica il database dal server remoto e lo aggiorna sulla macchina locale lo utilizzerete davvero ogni volta che vi serve controllare cosa accade con una copia aggiornata del DB remoto. Altrimenti per pigrizia (che Dio la benedica) di eseguire le solite operazioni di routine (per questo ci sono i computer!) eviterete di fare un importante test, o perderete un sacco di tempo se proprio avete voglia. E con questo... chiudo questo capitolo per aprirne uno molto piu' improtante.

Una applicazione che cresce come un organismo vivente

Un ciclo di sviluppo veloce crea sicuramente un vantaggio, ma non e' l'aspetto centrale del processo di creazione del software che e' chiaramente la trasformazione delle idee in codice funzionante. Ci sono diversi modelli che si possono seguire, e una discipina nota come Ingegneria del Software su cui nutro gravi scetticismi sembra rigurgitare a scadenze fisse delle ricette che dovrebbero permettere di creare processi di sviluppo piu' efficaci, ma che puntualmente sembrano smentite dalla pratica.

Il target a cui mi rivolgo e' diverso dall'azienda media, la startup del web 2.0 e' un piccolo insieme di persone altamente motivate che lavorano per loro e non per fare contento il capo. In media sono persone piu' competenti del dipendente casuale appena uscito dall'universita' con la mente farcita di Java e diagrammi con un immancabile retrogusto di progettazione dall'alto verso il basso.

Forse pero' la differenza piu' eclatante tra una startup di questo tipo ed una grande azienda e' il fatto che i programmatori e i progettisti coincidono nelle stesse figure. Questo da alla startup un vantaggio enorme. Infiniti meeting, incomprensioni, incapacita' di chi progetta di capire come poi va scritto il codice, vengono compresse ed eliminate nelle idee che passano in una frazione di secondo nella mente del progettista-programmatore. Le aziende hanno bisogno di credere che separare le fasi di progetto da quelle di sviluppo e' una buona idea, per loro e' quasi dogmatico perche' accettare il contrario significa capovolgere l'idea di managment con una struttura piramidale che e' alla base del loro sistema di potere.

Che loro ci credano o no questa differenza dona ad un pugno di ragazzi una forza inaudita di competere, specialmente se come conseguenza al fatto che costituiscono una diversa entita' di creazione del codice, adottano un diverso modello di sviluppo.

Qual'e' un buon modo per sviluppare software in una situazione del genere?

Il bi-pensiero dell'architetto del software

Non credo nella progettazione dall'alto. Non credo nelle figure separate del programmatore e del progettista. Credo nella scrittura del codice mista al progetto, che parte dalla base della applicazione per costruire un livello di astrazione sopra l'altro.

Non c'e' errore piu' grande che disegnare l'applicazione dal generale al particolare: i mattoncini sembrano non corrispondere mai, e nel processo di adattamento ti trovi con una struttura che non puoi mai cambiare a sufficienza quando in corso di sviluppo (immancabilmente) ti accorgi che avevi fatto degli errori e volevi in realta' sviluppare qualcosa di diverso.

Cio' che serve invece e' pensare l'applicazione nelle sue parti piu' basilari, costituenti, quelle che qualunque sia la reale evoluzione della applicazione durante il suo sviluppo in qualche modo saranno necessarie con buone probabilita'.

In un sistema di social bookmarking ad esempio si potrebbe iniziare a scrivere del codice che permette di inserire un elemento bookmark in un database SQL, che ha un titolo, una URL, e dei tag associati. Poi si possono scrivere delle funzioni che dato un bookmark aggiungono e rimuovono una lista di tag da un bookmark. Si puo' continuare scrivendo una funzione che conta quante persone hanno dei bookmark con una data URL, e magari una funzione che dato un array associativo con i dati di un bookmark fa il rendering in HTML. In pseudo-codice queste funzioni somiglierebbero magari ai seguenti prototipi:
getUrlById(url); # Ritorna un ID unico nel tempo per la URL in input
addBookmark(urlId,title); # Inserisce e ritorna l'ID del bookmark inserito
addTag(bookmarkId,tagList); # Associa i tag in tagList al bookmark
delTag(bookmarkId,tagList); # Rimuove i tag in tagList dal bookmark
bookmarkHtml(bookmark); # Fa il rendering in HTML di un bookmark
e cosi' via. Dopo aver edificato questo livello di base si puo' costruire il livello successivo. Ad esempio utilizzando le prime quattro funzioni si puo' scrivere una funzione postBookmark(url,tagList,user,...) che viene chiamata dall'interfaccia di inserimento di un nuovo bookmark, e cosi' via. L'applicazione e' edificata su diversi piani. Se domani si vuole modificare l'applicazione per supportare un database non relazionale basta cambiare il livello piu' basso. Se tra due mesi voglio stravolgere l'aspetto della mia applicazione le funzioni di base mi consentiranno di non riscrivere nuovamente il codice per aggiungere o rimuovere un tag o un bookmark, basta alterare i piani successivi.

Allo stesso tempo pero' bisogna applicare una sorta di bi-pensiero. Mentre si modellano i dettagli della applicazione, si deve avere in mente il disegno generale. Non i dettagli implementativi di tutte le parti, ma il modo in cui esse dialogano, le finalita' della applicazione, il modo in cui l'interfaccia dialoga con l'utente. Questa idea generale e' destinata a modificarsi continuamente man mano che le basi prendono forma. Scrivere codice fa venire nuove idee, e iniziare ad usare la prima basilare versione dell'applicazione ne fa venire ancora altre, e cosi' via in un processo piu' o meno senza fine.

Il piu' semplice programma possibile

Uno degli ingredienti di questo modo di sviluppare codice e' usuale argomento di discussione nei blog che si occupano di web 2.0 in questo periodo: avere una versione basilare dell'applicazione funzionante il prima possibile. Se hai avuto una idea che ti piace, e stai iniziando a scrivere il codice, prima rendi questa idea cosi' semplice che puoi finire con una versione funzionante nel giro di alcuni giorni.

Questo consiglio viene spesso dato per questioni di marketing (prendi dei consigli dagli utenti, sviluppa velocemente sotto la pressione di utenti attivi sul sito). Sembra una buona idea ma dal punto di vista dello sviluppo non e' un problema di marketing, il problema e' la semplicita'. Scrivere il programma piu' semplice inizialmente e' una buona strategia di sviluppo: la strategia che usa anche la natura che ha avuto piu' tempo di noi per pensare ai modi di creare strutture complesse. Le cose partono semplici, crescono, le parti si specializzano e diventano via via piu' articolate.

Se cerchi di scrivere una cosa complessa dall'inizio cadi nella trappola del top-down, tutto diventa complicato e poco tangibile, hai scritto migliaia di righe di codice e non puoi vederne funzionare neppure mezza. Un programma semplice puo' essere esteso in maniera armonica, una dopo l'altra le sue parti possono specializzarsi creando nuovi livelli di complessita' ed astrazione che risulteranno piu' gestibili se si evolvono in maniera naturale.

Da questo concetto ne segue uno simile ma non identico: quando hai la prima versione non congelare tutto per cercare di fare la versione 2.0 con un sacco di nuove features. Un albero non cresce per versioni successive... allo stesso modo e' possibile lavorare sui sorgenti per pochi giorni o anche alcune ore, fare una nuova feature gia' funzionante e metterla in produzione dopo un po' di test. Non metti alcun limite alla complessita' finale che potrai raggiungere ma invece di procedere per salti la tua applicazione cresce in maniera graduale.

L'aspetto puo' attendere

Un errore classico che si rischia di commettere e' quello di dare troppa importanza all'aspetto grafico nelle prime fasi. i CSS ci hanno dato un po' di liberta', consentono di utilizzare l'elemento DIV e avere solo l'idea di quali parti logiche avra' una data pagina mentre programmiamo, non serve curarsi dell'aspetto reale che avra' alla fine. In realta' questo non significa che non devi curarti della interazione con l'utente sin dall'inizio: cerca di creare le pagine di inserimento e di consultazione dei dati in maniera logica prendendo come utente di riferimento te stesso. Io credo che sia un errore molto grave cercare di disegnare un software per un utente piu' stupido di te perche' pensi che alla fine la massa non e' molto informatizzata: le interfacce stupide tendono ad essere scomode anche per chi non e' bravo ad utilizzare gli strumenti informatici, e in ogni caso per quanto ti sforzi non riuscirai mai a pensare come un utente medio.

Una linea guida che invece sembra funzionare bene e' cercare di creare una interfaccia semplice. Se una funzionalita' servira' in maniera saltuaria all' 1% ma ha un impatto sulla complessita' per tutti gli altri e' meglio non aggiungerla.

Alla prossima

Tutto questo ovviamente... nella mia modesta opinione e nella meno modesta opinione (perche' piu' autorevole quanto meno) degli autori di tutti gli scritti che ho letto e che mi hanno contaminato negli ultimi anni.

p.s. dimenticavo... mentre scrivi software, divertiti.

10842 views*
Posted at 21:08:29 | permalink | discuss | print
Do you like this article?
Subscribe to the RSS feed of this blog or use the newsletter service in order to receive a notification every time there is something of new to read here.

Note: you'll not see this box again if you are a usual reader.

Comments

comments closed